On Bash History Substitution

There are some things in every profession or area of expertise that seem so obvious to you and most people around you. However, there was a point in your career where you did not know this yet and somebody taught you.

A brief discussion on the MacAdmins Slack yesterday led to such a moment. A bash shortcut that seems so obvious to me and probably to many who read this, but again, not everyone knows. And it was someone’s lucky ten thousand moment to learn an wonderful shell shortcut:

sudo !!

Re-visit your (shell) History

The bash shell remembers a certain number of commands that you entered. This way you can ‘recall’ them with the up arrow or the ctrl-R keystroke.1

There are also history substitutions to save typing. And not only do they save typing but these automations, like tab-completion will make you type less errors.

Sudo Make me a Sandwich2

So imagine you typed

$ profiles -P
profiles: this command requires root privileges

and the system helpfully reminds you that you need root privileges to run this. You could retype the entire command after sudo. You could also hit up-arrow, ctrl-a and type sudo[space][return], which is some improvement.

Or you just type

$ sudo !!
sudo profiles -P
Password:
There are no configuration profiles installed

The shell will substitute the !! (‘bangbang’) with the previous command. The shell will print the entire command after the substitution (this can be really helpful when things don’t work as expected) and then immediately execute the full command.3

Personally, I only ever use this with sudo. I could imagine there are other uses, maybe with ssh user@host -c.

You can also just type !! to repeat the last command, I find ‘up-arrow, return’ to be more intuitive (and one keystroke less).

Historical Arguments

The other history substitution I use is more specific, but still has its uses. The shell substitutes !$ with the last used argument. The catch here is that if the previous command had multiple arguments, !$ will be the very last argument on that line, not all the arguments.

Some examples:

$ mkdir -p MyNewInstaller/payload
$ cd !$
cd MyNewInstaller/payload
$ touch postinstall
$ chmod +x !$
chmod +x postinstall
$ pkgbuild --component /Volumes/Firefox/Firefox.app --install-location /Applications Firefox-51.0.1.pkg
$ cp !$ /Volumes/SoftwareArchive/Mozilla/
cp Firefox-51.0.1.pkg /Volumes/SoftwareArchive/Mozilla/

(Most pkgbuild examples are easily dated with the Firefox version.)

If the last command had no arguments, !$ will be the ‘zeroth’ argument, the command itself:

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.3
BuildVersion:   16D32
$ !$
sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.3
BuildVersion:   16D32

More History

There are more bash history substitutions. I found a helpful StackOverflow post with examples here. Most are a bit too cumbersome to actually use the frequently.

Though, I myself had a ‘lucky ten thousand’ moment, since I did not know about !* (all the previous command’s arguments) and !^ (the previous command’s first argument) until today.

Path Through History

This is not directly related to the shell history, but `cd` also remembers your previous location in the file system and you can quickly jump back with `cd -`.

$ cd ~/Projects
$ cd ~/Library/AutoPkg/
$ cd -
/Users/armin/Projects
$ pwd
/Users/armin/Projects


  1. hit ctrl-R and type some text, to get the first command in your history containing that text
  2. If you do not recognise that reference, you are one the ten thousand, again.
  3. Yes, technically, sudo !![return] is the same number of keystrokes as up-arrow, ctrl-a and sudo[space][return] but I still find it easier.

One thought on “On Bash History Substitution”

  1. bind ‘”\e[A”:history-search-backward’
    bind ‘”\e[B”:history-search-forward’

    Put this in your bash .profile. It binds to the up and down arrows history search commands.
    Now enter in your prompt, e.g., “cd” (w/o quotes). You navigate with the up and down arrows through your history considering only the commands that start with “cd”.
    Very useful indeed for commands where arguments change often like “cd” and also why history search with Ctrl-R doesn’t let you choose.

Leave a Reply

Your email address will not be published. Required fields are marked *