The shell command history is a prime example. Even a newborn knows to use the up and down arrow keys to recall commands, and most toddlers are piping
lessto perform manual searches for complicated commands they don't feel like recreating. But if you ever have the chance to stare over the shoulder of a grey-bearded shell guru, you'll see that the true masters use several different techniques to pull up commands in the most efficient way. The following report was compiled from the sage advice given by these mysterious wizards.
We'll start with the classic C Shell history syntax. You've no doubt used an exclamation point to re-execute a command from the history list, like
!42, or have used the double exclamation to execute the previous command,
!!. You can get pretty fancy with the available event designators, for instance,
!-3will execute the command three commands ago. And
!echowill execute the most recently run command that began with echo.
This syntax has always scared me, as I could see myself executing a dangerous command without realizing it, especially if I'm in a hurry. But there is a safer way to use it, just include the
:pmodifier, which displays the command, but doesn't actually execute it. For instance, if the last command executed was
echo Hello, typing
!!:pwould preview the command without running it, so you'd see
echo Hello, instead of
Hello. This allows you to make sure it's the command you intended to recall, and you can simply press the up arrow to execute it.
!syntax can be really useful in a grep-the-history-file workflow. Say I grep for the last time I issued an esxcfg-firewall command:
history | grep esxcfg Output: 203 esxcfg-firewall --openPort 9090,tcp,in,SimpleHTTP !203:p Output: esxcfg-firewall --openPort 9090,tcp,in,SimpleHTTP
Now if I hit the up arrow, the command is put on the prompt, and I can edit it to add a different port, etc.
If you'd like to delve deeper, google c shell command history
Another method for recalling commands from the history list is the bash built-in Fix Command, invoked with
fc. To get the skinny on
fc, bring up the man page for the built-in commands, with
man builtins. Invoking fc with the
-loption will print the last 16 commands. You can also specify a range of history commands to display, like:
fc -l 208 234
fccommand can come in real handy when you are trying to recall and edit a whole series of commands. For instance, say you remember adding an alias to your .bashrc file, and you want to add an additional alias using the same series of commands to make sure it was configured properly. You recall using a
cpcommand first to backup the .bashrc file, and specifying 'cp' as a search string after
fc -lwill print the history list beginning with the last occurrence of the command that matches the search string:
fc -l cp 110 cp .bashrc backup_bashrc 111 echo "alias lax='ls -lax'" >> ~/.bashrc 112 cat .bashrc 113 . .bashrc 114 lax
To create an
fcwith a range of history events to copy them all into the default editor, which is set to vi in the ESX console:
fc 110 114
Running the above command will copy the history events 110 through 114 into a vi editor session, where they can be modified to create the alias for
ZZin vi will exit the editor and execute all the commands in the buffer: backing up .bashrc, creating the alias, cat'ing the file to see the change, restarting bash, and finally testing the new
laalias. I don't use
fcvery often, but for scenarios like this it is a great tool to be familiar with.
vi Command Editing
The next two methods for using the command history rely on functionality provided by the GNU Readline library, a standard set of C functions available to any application, including bash. There are two editing and key-binding modes, emacs and vi. The default mode is emacs, and you can see what mode your shell is in now by typing:
set | grep SHELLOPTS
If you're in the default emacs mode, you'll see emacs in the colon-separated list of options. To change this to vi mode, type:
set -o vi
Now if you check the
SHELLOPTSvariable, you'll find emacs has been replaced with vi. I'm a vi kind of guy, so I always add the set option to my .bashrc file, with a command like:
echo "set -o vi" >> ~/.bashrc
And source .bashrc again to get the changes:
We'll look at how to use the Readline library in vi editing mode with an easy example. I've just grepped the default word file that comes with the service console for every word that begins with 'a' (this word file is not present in ESX 4.0). I'll do the same for the letters 'b' through 'f', and if I execute
history, this is the output:
[admin@esx02 admin]$ history 1 grep ^a /usr/share/dict/words 2 grep ^b /usr/share/dict/words 3 grep ^c /usr/share/dict/words 4 grep ^d /usr/share/dict/words 5 grep ^e /usr/share/dict/words 6 grep ^f /usr/share/dict/words 7 history
Back at the shell prompt, we can search through the history list for the last instance of the grep command. Press
Escto enter vi command editing mode, and then use a forward slash to search, just like in a normal instance of vi:
After hitting return, we should find
grep ^f /usr/share/dict/wordshas been placed on the command line. Pressing
nwill iterate through each grep command until the first instance is found -- the grep command for words starting with 'a'. Continuing to press
nwill do nothing now, as we've reached the end of the matches for grep. However, pressing
Nwill now iterate through the grep matches in reverse, working its way to the most recent grep command. This is handy and easy to remember,
Nfor next, forward or backward through the history list.
Of course, the whole point of accessing the history list with this method is to easily edit the commands before executing them. If we wanted to modify the grep command to find all words starting with 'fox', we can just press
Escat a bash prompt to enter vi command editing mode, type
/grep ^fto find the history entry where we searched for words starting with f, press
ito enter vi insert mode, edit the command:
grep ^fox /usr/share/dict/words, and press return to execute it. If we want to get every word that starts with ox, we just perform another search,
/grep ^fox, move the cursor over the 'f', and hit
x-- the vi delete character command -- to remove it.
This example is beyond goofy, but if you play around with this method, you'll find it to be very powerful and a huge timesaver.
If you press
Ctrl-rin your terminal, you'll get a curious looking prompt:
If you start typing a part of the command you wish to search through the history for, the command will appear after the prompt:
(reverse-i-search)`grep': grep ^f /usr/share/dict/words
This is the most recent grep found while searching back through the history list. If you press
Ctrl-ragain, you'll find the one before that:
(reverse-i-search)`grep': grep ^e /usr/share/dict/words
If you hit return, the command will execute. If you hit the left or right arrow keys, the command will be left on the prompt so you can edit it first.
reverse-i-searchprompt is a bash built-in named reverse-search-history. You can see that by default it is bound to the
Ctrl-rkey by issuing a
bind -Pcommand. If you look through the list, you should also see forward-search-history has been bound to
Ctrl-s. The forward-search-history command is a useful companion because if you play with
Ctrl-r, you'll notice that as it iterates through the history list, it remembers where it left off. So if you reverse search all the way to the start of the history file, you then have to cycle back around, even though the command you want was just one command more recent.
But there is a big problem with forward-search-history, it's bound to
Ctrl-sby default, which is also bound to the
sttystop output command, and
sttywill intercept it before anything has a chance to see it (if you already pressed
Ctrl-sand your terminal appears dead, just type
Ctrl-qto bring it back to life).
You can view the key bindings used by stty with this command:
But these two commands are too handy to let a little key binding issue get in the way, and we can easily add custom key bindings to our .bashrc file:
bind '"\C-f": forward-search-history' bind '"\C-b": reverse-search-history'
Just edit the ~/.bashrc file with the editor you prefer, add those two lines in, and re-source with:
Ctrl-bsearches back through the history file, and
Ctrl-fsearches forward, and you can toggle back and forth between them and the search term will remain. Nice!
Share your secrets
Hopefully you found a new trick to play with, and if you have your own history workflow, please leave a comment with the details.