Tips to help you move faster using shell history commands

Marco Fioretti points out the many time-saving uses of the shell history to help you work more efficiently.

Once you get the hang of it, the command line is faster than a graphical interface -- you can type faster than you can point and click.

Could you go even faster, that is, type less? And even when complete automation with a script is not possible, because you're working without a predefined goal or a fixed, predefined sequence of things to do? Of course, yes!

You can, indeed, avoid retyping long commands you had already entered (perhaps, to fix one typing error) just a few seconds before. All the command interpreters we call shells keep a history of everything you type and can expand (that is, retrieve to execute again, or edit) the commands you select. The easiest usage of the shell history is pressing the Arrow Up or Down keys, to scroll back and forth through the sequence of previously typed commands. Here are a few more concepts and tricks that will help you use the bash history in the most effective way. History expansion is not an all-or-nothing act, and it is easier to learn if you already know the name and meaning of certain terms before reading the extensive, but quite cryptic, official documentation.

Searching for the right command

History navigation is easier with CTRL-r <string>, which lets you search the history for the most recent command containing that string, and then either execute it immediately (by pressing Enter) or edit it (pressing CTRL-j or Escape). You can even tell the shell to search and edit a command at the same time: ^user2^user1^ means "substitute user2 with user1 in the selected command and re-run it."

This is only the first example of the fact that it is possible, after choosing one line of commands from the current history, to only reuse some parts of it. In practice, you can do this because the shell history knows about event or word designators and their modifiers.

An event is one complete sequence of commands previously entered as only one line. Something like:

cd; rm *~; ls-lrt

That is one event. You can designate events, that is, select them, in several ways, all starting with an exclamation mark:

  • !! means "the previous command"
  • Each command in the history has an index: to see the the indexes of all the commands, just type "history" at the prompt. You can tell the shell to re-execute the command with index N by typing !N.
  • !-N, instead, stands for "the command N places before the last". !STRING means "re-execute the most recent command starting with STRING." Add a question mark !?STRING to look for commands containing STRING in every position, not just at the beginning.

Which words make history?

Words are the parts, or arguments, of each command. I said parts because these are words in the shell sense, which is different from normal language. In this context, word means "one unique argument" that could be either one character string without spaces, or more of those strings, enclosed by quotes. Therefore, a command like:

echo "Ciao, TechRepublic users!"

only consists of two words, as far as the shell is concerned. Words are written, separated by a colon, after event designators. An example of using words in history is !!tar:2 which re-executes the second argument of the most recent command starting with tar.

The modifiers, instead, are single-character options. They change how the shell uses the words previously selected with their designators. Modifiers are also separated by colons, and you can use more of them together. The first modifier you want to learn and remember is :p, because it only prints the command, without executing it again. Very useful, if not mandatory, when you load from the history destructive commands, that may cancel or alter files that are not those you wanted to mess with. Other useful modifiers are h and t, that remove trailing and, respectively, leading components from path names:

  #> ls /home/marco/docs/work/bash_history.txt
  #> cd !!:1:h
  cd /home/marco/docs/work
  #> pwd

See what I mean? The second command uses as argument for cd the heading part (:h) of the first word (:1) of the previous event in the history (!!).

My favourite history-related shell variable

In my opinion, the coolest thing in shell history support is the configuration variable called HISTIGNORE. Its content defines what must not be saved in the history. When I first discovered HISTIGNORE years ago, I thought it was worthless, but I was wrong. Setting HISTIGNORE properly can save you a lot of time, if you do a lot of work at the prompt. Here's why. When you engage in long shell sessions, most of the commands will surely be navigation or housecleaning stuff like "cd .." (move to the parent directory) or "rm *~" (remove all backup files ending with "~"). There is no reason to save such commands in the history, partly because they clutter it, partly because they're so short that re-typing them takes almost the same time as recalling them from the history. Other stuff that's not worth saving are duplicates, that is, different runs of the same command. To tell the shell what commands should not be saved, list them in a colon-separated list inside HISTIGNORE:
  export HISTIGNORE="export HISTIGNORE="&:cd ..:rm *~"

By the way, & is the option that removes the duplicates. Trimming the history with HISTIGNORE makes it much easier and quicker to use. That's why I like it. Its only drawback is that it can make it (much) harder to reconstruct and analyze what you actually did. In other words, if you mainly need the history as a documentation tool instead of a time saver, leave HISTIGNORE blank. Otherwise, you won't have the answers to questions like "which directory was I in, when I typed rm *?"

The shell history can do many more things than what I have outlined here. These tricks, however, should make it much easier for you to learn everything else explained in the man page. Let me know how it goes!