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:
!* 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
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:
!! 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
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.