Taming Repetitive & Hard-to-Remember Console Commands
Or, “How to Quell Your Complicated and Repetitive Command-Line Actions with Easy Aliases and Short Shell Scripts”, if you like long titles.
Sometimes I feel as if I end up typing a single handful of command-line commands over and over again all day. That’s a bit of an exaggeration, but I do spend a goodly amount of my day at the command line. As part of my workflow, I always have at least three terminal windows open in order to run the various portions of whatever web app I have my hands on:
- One terminal running the front-end web server
- Another running the back-end service,
- And a third running a database inside of a Docker vm.
My little story about forgetfulness
The front-end and back-end processes can be launched once and left running for most of the day without any extra intervention. The Docker container running the database, on the other hand, needs to be stopped and re-started every time I or a coworker alters the format of the project’s database. If I don’t re-start the container, then I won’t see the changes that were made to the database.
Stopping and re-starting a Docker container isn’t a big deal, but it does require two commands:
1 2 | docker-compose down docker-compose up |
The problem? Neither command finishes instantaneously on my computer. I usually end up opening Chrome or Slack while I wait for the ‘down’ command to finish. But then I get distracted and forget to follow up with the ‘up’ command. I forget, that is, until I attempt to use the database again, only to find that it isn’t available. Gah!
My second story about forgetfulness
At work, I also need to create screen recordings from time to time in order to illustrate a new feature’s functionality, the result of a user-interface re-desing, etc.. After making a recording, I upload the video to a shared website so that other members of the team can view it.
Before I upload a video, however, I use a command-line tool called ffmpeg to convert it from its original, huge .mov format to a compressed .mp4 format. The command looks like this:
1 | ffmpeg -i input.mov -b:v 2048k -bufsize 2048k ffmpegOutput.mp4 |
As you may be able to guess, my annoyance here isn’t the number of commands, it’s the complexity. I can never remember this entire command (I had to look it up in order to write it here!).
Instead of quickly converting my videos and moving on my merry way, I found myself browsing the internal knowledge-base website at work in a search for the correct magical incantation every time I needed to compress a video.
Sound familiar?
If either of my two command-line annoyances sound familiar, let me divulge two techniques that should help streamline your command-line workflow while allowing you to get your feet wet in bash scripting at the same time:
1. Shorten long lines with a Bash alias
Bash (the default shell on Mac OS X and most Linux distributions) comes with a nifty feature called aliasing. Essentially, aliasing allows you to re-name a command to something new (usually something shorter and more memorable).
In my case, instead of typing the following big, long command for compressing a video:
1 | ffmpeg -i input.mov -b:v 2048k -bufsize 2048k ffmpegOutput.mp4 |
I would like simply to be able to type ‘compress’:
1 | compress |
To set an alias for Bash, you will need to edit a file in your home directory called .bashrc. Chances are, you already have a version of this file in your home directory, but if not, you may need to create it.
This is a hidden file, so it won’t normally appear visibly in your home folder. To find out if you actually have it, you can open a terminal and run the following command, substituting your username where it says ‘jonathan’ (unless your username is jonathan, of course):
1 | ls -a /home/jonathan/ | grep .bashrc |
This command lists (ls) with additional info (-a) the contents of the /home/jonathan/ folder, then searches (grep) the contents for the text ‘.bashrc’. If you do indeed have a .bashrc file, your terminal will print out ‘.bashrc’ on a new line.
1 2 | ls -a /home/jonathan/ | grep .bashrc .bashrc |
If this line doesn’t appear, then you will need to create the .bashrc file yourself. Don’t worry, it’s just a plain old text file named .bashrc, so you can create it with Vim, Nano, TextEdit, or some other text editor. Just make sure you put it in your home directory.
Now, to actually set an alias, you need to add a fancy new line to your .bashrc file. The line needs to have the following structure (note the quotation marks!):
1 | alias newaliasname="command you want to shorten" |
You will need to replace the part of the line that says ‘newaliasname’ with whatever you want your alias to be called. This is where you will want to pick something short and memorable!
Similarly, change the bit between the quotation marks to whatever long command you are re-naming.
I recommend that you stick your new alias line right at the bottom of the file so you can find it later if you need to.
In the case of my ffmpeg example, the line I would add to .bashrc is the following:
1 | alias compress="ffmpeg -i input.mov -b:v 2048k -bufsize 2048k ffmpegOutput.mp4" |
Now, if you close and re-open your terminal, you should be able to type your new alias any time you would normally have to write out your old, long command!
2. Combine multiple commands with a script or alias
Remember my first example, where I complained about having to remember to run ‘docker-compose up’ after running ‘docker-compose down’? There are actually two (or more!) ways that we can make Bash automatically run the second command after the first finishes.
The quickest way would be to use another alias, but this time put our two commands inside of the parenthesis, with a double ampersand (&&) between them, like so:
1 | alias dcdup="docker-compose down && docker-compose up" |
Chaining together commands like this is really easy, and it’s a technique that works particularly well in this example. If you ever want to combine more than just a few commands, however, you will likely be better off creating a Bash shell script.
A Bash script is just a list of command-line commands that run one-after-another when invoked. I prefer Bash scripts to aliases when I am combining several commands because it means I can organize the commands into their own file. I like that better than dumping them inside .bashrc, where they might sit near a bunch of Bash configuration commands. If you take some time to learn the programming features that Bash offers, then Bash scripts also offer more exciting possibilities than aliases can give, such as choosing between two commands based on the result of a previous one.
I won’t dive into the programming control structures that Bash offers, but the basic process for writing a shell script goes like this:
- Create a text file that has ‘#!/bin/bash’ as the first line
- On the following lines, type the commands that you want the script to carry out.
- Save the file (conventionally, Bash scripts end in .sh).
- Make the file ‘executable’ by running ‘chmod +x thenameofyourscript.sh’. (this gives you permission to run the script.)
Once you have your finished script, you can run it from the directory that it is stored in by typing its filename preceded by a period and a forward slash:
1 | ./thenameofyourscript.sh |
If you would like to be able to run your script from any directory other than the one in which it is stored, you will need to copy or move your script to one of the directories included in your terminal’s path. Your terminal’s path is a list of the directories in which your computer searches for the programs whose names match up with the commands you type into the terminal.
You can see which directories are part of our path by running the following command:
1 | echo $PATH |
In my case, the directory /usr/local/bin/ is part of my path, so I often put my scripts there.
Once your script is in one of your path directories, you can run it from any directory simply by typing its name (including the .sh part, if you named it such).
To give you an idea of what you can do with a shell script, here is one that calls ffmpeg:
1 2 3 4 5 6 7 8 | #! /bin/bash if [ $# -eq 2]; then # Invoke ffmpeg with first parameter as input # and second parameter as output ffmpeg -i $1 -b:v 2048k -bufsize 2048k $2 else # If no parameters given, use some default file names ffmpeg -i input.mov -b:v 2048k -bufsize 2048k ffmpegOutput.mp4 |
It is similar to the alias I made earlier, except that this script allows me to specify the input and output file names as paremeters if I don’t want to use the default ‘input.mov’ and ‘ffmpegOutput.mp4’ filenames that were hardcoded into the alias.
What if I don’t have any repetitive commands?
Even if you don’t have any particularly cumbersome command-line activites as part of your workflow today, you may find yourself struggling in the future to remember an obscure command or loathing a particularly repetitive sequence of commands. Now that you’ve been exposed to concept of aliases and scripts, you should be able to recognize situations in which a good alias or script might make the difference between getting where you want to go immediately and scouring the web for the command you just can’t seem to remember.