Skip to main content

Parsing Bash history in Linux

The history command isn't always about reducing key presses. Find out how you can leverage command history into more efficient system administration.
Image
Parsing bash history

In Bash, the history command is the key to not just understanding what you've been doing during your shell session, but also to raw material for new commands you want to run in the future. History is especially useful when you're running a headless server. If you have no mouse, you can't easily copy and paste the last half of a complex command to run a slightly modified version of it, so you have to type the whole thing over again - unless you use history modifiers.

Keep history out of your history

Before discussing history, here's a tip for keeping literal history entries out of your history.

The problem with using the history command to review your history is that it adds an entry to your history file. These entries not only clutter up your history, but they also cause each line to regress "farther" away from your current position.

$ cd example-project
$ ls
bin doc src
$ touch doc/LICENSE
$ history
102 cd example-project
103 ls
104 touch doc/LICENSE
105 history
$

A moving target is tough to track and hard to hit, so if you plan on seriously using your history to speed up your Bash interactions, add this configuration to your .bashrc file:

export HISTCONTROL=$HISTCONTROL:ignorespace

This configuration prevents any command preceded by a space from being added to your Bash history.

$ cd example-project
$ ls
bin doc src
$ touch doc/LICENSE
$   history
102 cd example-project
103 ls
104 touch doc/LICENSE
$

[ Free online course: Red Hat Enterprise Linux technical overview. ]

History events

The basic structure of shell history starts with a list of events. An event is anything you've entered into the shell, followed by the Return or Enter key. Each event is assigned an index number (a line number), and you can view and recall events by using event designators.

For instance, to recall a specific line item in your shell history, use an exclamation point (!) followed immediately by the number of the history line:

$   history
102 cd example-project
103 ls
104 touch README
$ !103
ls
bin doc README src

You can also refer to past commands relative to their position in the history listing. For example, to run a command two lines in the past, think of your current prompt as one greater than the last line in history, and then subtract the number of the line you want to execute from your current line number. In this example, line 103 is the target line and 105 is the current prompt (for a delta of 105-103=2):

$   history
102 cd example-project
103 ls
104 touch README
$ !103
ls
bin doc README src

You can save a whole character if you just want to re-run the last line. The expression !! and !-1 are synonymous:

$   history
102 cd example-project
103 ls
104 touch README
$ !103
ls
bin doc README src

Notice the command itself is repeated in the output when history is repeated. For brevity and clarity, such output is excluded from the remaining examples in this article.

Word designators

Bash has features to help you parse through your history using word designators, and tools to allow you to modify the commands in your history. It might help to think of your shell history as an indexed array, or even as a large history object consisting of lots of smaller items. Each word on each line of your history is indexed, starting at 0 (which is usually the command, but multi-part commands and environment variable prefixes are notable exceptions). You can access each index by adding a colon (:) to your line reference, followed immediately by the word designator.

Here's an example accessing the first word:

$   history
102 cd example-project
103 ls
104 touch README
$ !103
ls
bin doc README src

Because grabbing the first argument is common, the circumflex (or the "hat": ^) is an alias for :1.

$ echo foo
$ !!^
foo

On the other end of the spectrum, the dollar ($) sign represents the final argument:

$ echo foo bar baz
$ echo !!$
baz

Ranges of words

You don't have to settle for just one argument. You can extract ranges of an index, too. All arguments but the zeroth is available with an asterisk (*).

$ echo foo bar baz
$ touch !!*
$ ls
foo bar baz

This is actually a synonym for 1-$ (that is, the first argument to the last argument). As you might expect, you can arbitrarily define your range using this unabbreviated notation. This example references the second to fifth arguments, ignoring the first and final two arguments:

$ cp foo a.sh spacer.sh rev.bin sync.py baz ~/dest
$ cd ~/dest
$ chmod ug+x !-2:2-5
$ ls -l --classify
a.sh*  baz  foo  rev.bin*  spacer.sh*  sync.py*

If you just need to exclude the final single word in an index, you can provide a range with no termination:

$ cp foo a.sh spacer.sh ~/dest
$ cd ~/dest
$ chmod ug+x !-2:2-
$ ls -l --classify
a.sh*  foo  spacer.sh*

Using history for speed and precision

The history command isn't always about reducing key presses. You can use your Bash history to avoid accidentally excluding a file or folder from an important operation, or misspelling a filename. Get comfortable with the history command for more robust Bash interactions.

[ Want to try out Red Hat Enterprise Linux? Download it now for free. ]

Topics:   Bash   Linux  
Author’s photo

Seth Kenlon

Seth Kenlon is a UNIX geek and free software enthusiast. More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.