Boosting Interactive Bash Efficiency Through History Search Completion Editing

Most of us know about using the bang operator (!) to recall an entry from our bash history:

$ ! # repeat last command
$ !22 # repeat command 22

You can use “!:” followed by a number to substitute in arguments from previous commands. So, for example, to run the command “dosomething” on the first argument of the previous command:

$ dosomething !:1

The fc command is also very useful, opening up the default editor to let you edit previous commands. Saving and exiting will execute the command, while canceling the save (e.g., “:cq” in Vim) will abort.

$ fc # "fix" previous command
$ fc -10 0 # "fix" previous 10 commands

Things get really slick when using the CTRL-R and CTRL-S operations. These will incrementally match a string against entries in your shell history. For example, in my shell, typing CTRL-R followed by “mak” yields:

(reverse-i-search)`mak': make PREFIX=~/Documents/System/Environment/local/ install

While typing CTRL-R followed by “Pro” yields:

(reverse-i-search)`Pro': cd Documents/Projects/Miscellaneous/msbayes-ext/

From here, I can use CTRL-R or CTRL-S to scroll backward or forward through all possible matches. I can accept and execute the match by hitting ENTER, or accept the match but not execute it by hitting ESC (thus allowing me to edit the line). Hitting CTRL-C or CTRL-G aborts and returns me to the regular interactive shell. This incremental history search is fantastic! No more “history | grep something” followed by cutting-and-pasting and then editing. But often I have already started typing something, and then decide that I want to complete this from some entry in my history. In these cases, CTRL-R and CTRL-S will not be as efficient, as invoking them will result in me having to re-type the partially entered command. However, a few lines added to my “~/.bashrc” sets Bash’s readline options to allow my to call up completion based on incremental matching of my partial command against my shell’s history:

# Ctrl-p: search in previous history
bind 'Control-p: history-search-backward'
bind -m vi-insert 'Control-p: history-search-backward'
bind -m vi-command 'Control-p: history-search-backward'

# Ctrl-n: search in next history
bind 'Control-n: history-search-forward'
bind -m vi-insert 'Control-n: history-search-forward'
bind -m vi-command 'Control-n: history-search-forward'

The above settings are so as to get this behavior in both Vi-mode readline (my default) as well as Emacs-mode readline (readline’s default). Incidentally, while not history-editing specific, if you do not already know about the v or CTRL-X CTRL-E commands, you really should check them out. The former works in Vi-mode, while the latter works in Emacs-mode. Both of them open up the current command for editing in your default editor. In combination with the history search/completion options described above, it makes for a really potent and efficient Bash session: you can call up a previous command using CTRL-R/CTRL-N, hit ESC and then v or CTRL-X CTRL-E to open up the default text editor and tweak the line using the full power and sophistication afforded by the editor. Then, simply save and exit (e.g., “:wq”) to execute, or cancel (e.g., “:cq”) to abort. I use Vi-mode readline, and I have mapped CTRL-V to be able to call up Vim on my command when I am insert mode by adding the following lines to my “~/.bashrc“:

# Ctrl-v: (insert mode) switch to command mode and edit in vi
bind '"\C-v": "\ev"'