Use your shell’s history

Dear new developer,

The command line is a powerful tool, and writing shell scripts lets you write a series of commands once and replay them any time you like.

But sometimes you will write a series of commands without putting them into a script. This may be because you are exploring a problem or because you haven’t bothered to put together a script. No sense in making something repeatable if you aren’t sure exactly how you are going to repeat it.

But you may want to look back over past commands you have run, whether to run them again, modify them or even just remind yourself what you’ve done. For this, the shell history is very handy. Another time I often look at the shell history is when I am signing into a machine that I don’t visit very often. Perhaps there’s a command to look at some log files that I know, distantly, in the back of my mind, that I ran four weeks ago. I could have documented it, but maybe I didn’t. If I look in my command line history, I can see it.

All of the examples below are for the bash shell. Why? This is a very common shell, more modern shells like zsh are roughly compatible, and most importantly, this is the shell I know.

To use the shell history, first you have to know it exists and then you have to see it. You can also view the history by typing history in your terminal or at the shell prompt. Here’s an example from one of my shells:

  520  vi text.adoc
  521  git status
  522  git add text.adoc
  523  git commit -m "Initial pass at examples and documentation."
  524  vi text.adoc
  525  git add text.adoc
  526  git commit -m "Added one more example." text.adoc
  527  git push origin add-example-docs
  528  history

Each line is one command and is numbered. From here, I can repeat a command by number. So if I type !524 the command vi text.adoc will be run. I can also search for a certain string by using grep:

history | grep 'ssh' # will show all my ssh commands

The above commands are all pretty simple, but you can also link commands together. I often will do something like this:

cd directory && mvn jar && cp file.jar ~/place/jar/goes && cd otherdirectory

Once I get this command right, I can run it over and over again by referencing its line number. The && means that each command has to succeed before subsequent commands are run. So if the mvn command fails, the cp will not run.

This is fine for running tasks that are not going to change, but what about if I want to edit my previous commands. You can navigate the history by using the arrow keys (there are other ways as well, but arrow keys are the default and most accessible). Each up arrow takes you one step further back in your history, and each down arrow takes you one step forward. At any point you can edit the command you are currently on and hit the enter key and run it.

The bang shortcuts

If you know you want to edit whatever command you ran previously, you can use some bang operator shortcuts (so called because “bang” refers to a !). Let’s say I want to get the latest main branch from my git repo.

Suppose I run the command:

gti fetch origin main

I see an error message because I misspelled git. Whoops. I can type:

git !*

!* expands to everything except the first argument in the previous line, so this is the command that is run:

git fetch origin main

Now I want to check out the main branch

git checkout !$

!$ refers to the last argument of the command, so main in this case. The command which is run is then:

git checkout main

Knowing these shortcuts will help you avoid tediously hitting the arrow keys, editing the previous command and re-running it. There’s one more bang shortcut which is very useful. Suppose I want to install something on a linux box:

apt-get install pkgname

Whoops again! I’ll get an error message because I’m not root. Easily fixed by typing:

sudo !!

!! expands to the entire previous command, so what is actually run is:

sudo apt-get install pkgname

I use this very often when working with linux on the command line.

Controlling what is saved

You can control how much history is kept. Sometimes on my development machine I keep an infinite amount of it. I’ve also been on production instances where there was no history kept, I believe as a security measure. Remember, any command you run, including opening up a database connection using a command line client, will be kept in the history buffer. If you put a password on the command line, it will be recorded in this history.

To avoid that, don’t put the password on the command line. Another trick is to set the HISTCONTROL variable. When this is set to ignoreboth any command you type with a space in front won’t be stored in the history.

export HISTCONTROL=ignoreboth # previously set
echo "hello" # included in history
 echo "goodbye" # not included

You can also control explicitly how many commands to store in your history with the HISTSIZE setting:

export HISTSIZE=2000 # will save 2000 commands

Finally, be aware that all the bash shells opened share the same history file. Unless you take special care (as outlined here) the last one closed will win.

Being able to quickly re-run commands you’ve built previously, whether unchanged or not, can be helpful when you are navigating around the command line in pursuit of your software goals. Knowing a few history commands lets you do so.

Sincerely,

Dan

4 thoughts on “Use your shell’s history

  1. A few lines from my ~/.bashrc. I like to keep my history a long way back!

    # Simplify history searching, type partial command and press up/down arrow.
    bind ‘”\e[A”: history-search-backward’
    bind ‘”\e[B”: history-search-forward’

    alias h=’history 20′

    export HISTSIZE=100000
    export HISTFILESIZE=100000

    # Ignore some commonly used but historically uninteresting commands
    export HISTIGNORE=”&:bg:fg:ll:h”

    # Include date and time in the history listing
    export HISTTIMEFORMAT=”$(echo -e ${BCyan})[%F %T]$(echo -e ${NC}) “

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.