Command-line for Editors, Part 3: One-liners vs. Scripts

The commands we discussed in Part 1 and Part 2 used semicolons to string together multiple commands at once. This method of command-line scripting is often referred to as a “one-liner“, because they’re typed directly into the command-line prompt, instead of first saving them inside of a standalone script file. One-liners are great for highly specific, one-off command line tasks that you probably won’t need to run again in the future. You don’t need to figure out where to save the script file or change the permissions on it so it runs properly.

The drawback to one-liners is that they can be cumbersome to debug or understand, especially if you’re still not comfortable with command-line scripting. And once you close your Terminal, the only way to get the command back is to dig into your shell’s history file to find it.

When people talk about command-line “scripts”, they’re generally talking about an ASCII or UTF-8 based plain text file, containing all of the commands and logic required to perform the task that the script was written for. Keeping all your commands in a script file, instead of running them as a one-liner offers many advantages:

  • It makes your code much easier to read and understand, which is particularly important when debugging.
  • It lets you add non-executing comments to your code, so you can annotate specific parts of your code for reference purposes.
  • It lets you use whitespace and indenting to structure your code in a more logical, visually pleasing way.
  • It allows your code to be stored in a version control system, like Git, which allows you to both document and infinitely backtrack any changes to your code, if necessary.
  • Scripts allow you to send filenames, text or other information as input data for the script to work on. This means you don’t need to hardcode things like folder paths or filenames into the script itself, which greatly enhances the flexibility and portability of the script.

Converting our one-liner into a proper script

If we convert our one-liner examples into an actual Bash script, it looks like this:

#!/bin/bash

destination_folder="/Volumes/path/to/alphabetized/folders"

for i in {A..Z}
   do
      mkdir "$destination_folder/$i"
   done

shopt -s nocasematch
for dir in *
   do
      cp -r "$dir" "$destination_folder/${dir:0:1}"
   done

The first thing you’ll notice is very first line of the script, which in Unix-speak is called a “shebang“. It tells the command-line shell to use a specific program (generally referred to as “binaries” or “executables” in Unix-ese) to execute the contents of the script. In this case, the defined shebang will use the bash executable, located in the /bin folder, to run the contents of the script.

You may also notice that all of those semicolons that we used to separate commands in the one-liner are gone! In a proper Bash script, commands are separated by newlines instead of semicolons, by default.

The next obvious difference is that we’ve used indentation to show logical relationships between commands and control structures (e.g. For loops). This makes the code much easier to understand. You can clearly see that the mkdir and cp commands belong to their respective For loops, and if you wanted to add more commands to each loop, it’s obvious where to insert them in between the “do” and “done” keywords.

One new thing I’ve added to this example is the use of a variable named “destination_folder” to contain the fully qualified path to the folder where you want to write the new folders & files to. Since you’re writing to that base path more than once, it’s a lot more efficient to just assign a short variable name to that path, and then use the variable everywhere in the script where you need to reference that path, instead of hardcoding the path every single time. And if you decide later that you want to write to an entirely different folder, all you need to do is update the path that you’ve assigned to the destination_folder variable, and that new path will be automatically assigned everywhere else in the script.

As you can see, although it’s still useful to know how to write one-liners, keeping your command-line tasks within an executable standalone script offers many benefits over just trying to type everything directly into the Terminal shell prompt.