Expand your command line skills
As a programmer you have a wide array of potential tools at your disposal for creating, manipulating, and executing code. You may decide to utilize an IDE (integrated development environment) such as Eclipse or Visual Studio. And no doubt, such programs can be very useful. You may be able to integrate a large portion of your taskflow with your IDE of choice, but there will undoubtedly be times when you still need to use the command line. Leveling up your command line skills is always a fruitful investment, even if you primarily use an IDE for development.
You may also decide to permanently dwell in the terminal and really see dividends for investing in these skills. I tend to work nearly exclusively in a terminal window, using vim as my text editor of choice, and leveraging tmux for its terminal multiplexing and persistent sessions which I can attach to from any device. The familiarity of working within the terminal makes it pretty easy to hop onto other machines and find my way around, given the ubiquity of the core Linux utilities which I tend to most rely upon in my workflow.
To this end, I’d like to go through some of the practical use cases for history expansion that I’ve found particularly useful on a regular basis. As mentioned, this is for bash, which happens to be the default shell on Linux and MacOS terminals. Another shell that I see pop up with regularity from time to time is zsh. I have not verified all of the examples, but I think most of what we’re covering here is also supported in zsh, though YMMV.
Damage control
While playing around with history expansion, you may want to exercise caution so that you don’t do anything unexpected… Let’s set the option in the current shell so that pressing <Enter> with a history expansion will expand the shortcut first, and make you press <Enter> again, so that you don’t accidentally execute an incorrect command which might cause damage.
We’ll come back around at the end of the article to discuss how to have this option set for you automatically going forward.
My Favorite Four
!! (previous command)
Entering “!!” in the command prompt will expand to the last command you executed. This is particularly useful whenever you want to execute the same command you entered, but with super user privileges.
!$ (last argument of previous command)
Entering “!$” in the command prompt will expand to the last argument of the last command you executed. This is probably the history expansion I find myself using the most. It is a pretty common use case to create or modify a file, and then pass it as an argument to another utility, e.g., staging a new or modified file for commit or executing a modified script. I also find myself using it quite often when copying a file to another file and then modifying that new file.
^old^new OR !!:s/old/new (substitute old pattern for new pattern in previous command- first/one match only)
Maybe you typed out a big command with a bunch of arguments, and screwed up on one of the filenames or command options. These history expansions are useful for correcting course.
Note that they only substitute the first match, so if there are multiple instances of the same pattern that you want to modify, you’ll need to use the next history expansion instead.
!!:gs/old/new (substitute old pattern for new pattern in previous command- all matches)
Like the previous !!:s/old/new substitution, but with an extra option “g” for “global” substitution.
Handy modifiers extending the fabulous four
!<number> (the <number>th command)
All of the previous “!!” examples can also be used within the general !<number> pattern, in which the number can be a positive or negative number. Via the command “history” you can see your command history and the commands’ associated numbers. Let’s invoke the history command and execute a particular previous history command.
You can also specify a negative number, e.g., !-2 is the command two commands ago.
!!:<number> (the <number>th argument of the previous command)
Besides the “!$” expansion to get the last argument of the previous command, you can use the !!:<number> expansion to use the <number>th argument(s) from the previous command, the command itself being the 0th argument.
You can also specify a range of arguments via !!:<number>-<number>.
You can also use * modifier to indicate all arguments, with !* being a shortcut for all arguments from the previous command.
!{string}/!?{string} (the previous command starting with/containing the specified {string} value)
I tend to use bash’s reverse search functionality (<control>-r) for this kind of thing, but you can also use this handy history expansion technique.
Starting with match:
Contains match:
Configuring the histverify option
You’ll want to add the “shopt -s histverify” we executed above to your shell initialization file so that this is enabled by default.
Which file is this? It can be a bit complicated (always many variables and exceptions between distributions), but the intention is that ~/.bash_profile is used for login shells, i.e., when you login sitting at the machine or remotely via ssh, while ~/.bashrc is used for initializing a new shell instance while you are already logged in. It is a common practice to put most things in your ~/.bashrc, and to source your .bashrc file from your .bash_profile file with something like the following:
Hack away~