How to use the bash command line history
We will take a look at the bash history mechanism and it's persistence and learn how to ensure that it contains information that is relevant to our use of the command line. Learning all of this will help us to better manage its contents.
Jul 23, 2014 • 3 Minute Read
The bash history mechanism is persistent and remains with us even after reboots, so we want to ensure that it contains information that is relevant to our use of the command line. Let's take a look at how it all works, so that you can better manage its contents.
First, it's important to point out that each Linux user has his personal command line history stored within the home directory as ~/.bash_history.
This history can be accessed via the up arrow key; however, doing this through many entries isn't exactly efficient. Thankfully, there are other ways to retrieve previously entered commands:
- !rc to run the last command that starts with the characters “rc”
- !$ to repeat the last argument, for example:
◦ cp /mnt/sales/fred.xml /data/fred.xml
◦ vi !$
We would first copy the /mnt/sales/fred.xml to /data/fred.xml, then edit the last argument with vi . The last argument being, in this case, /data/fred.xml.
- cntl + r can be used to reverse search for text in the history file (starting from the most recent)
- !?etc Can be used to run the last line in the history that contains etc anywhere as a string, like in the following command:
◦ ls /etc
◦ ls /usr
◦ ls /home
◦ !?etc (will run ls /etc)
Now, let's read on and see why it so important to make sure that we store only relevant information in our history.
The history command
To understand how the bash history works we must first tackle the command: history. The command itself is a bash built-in. This simply means that history exists inside the bash program itself, /bin/bash. Using the command type, which is also a shell built-in, we can see that history is, in fact, a shell built-in. This is shown in the output, like this:
Using the history command without any options will display your current command history, made up from what has been read in from the .bash_history file (from your home directory) and commands entered within the session.
When you begin a new bash session, the contents of the history file are read into your current session history. When you logout or close that session, the session history is appended to the history file. The default shell option histappend is enabled by default in Raspbian on Raspberry Pi. If it's not enabled, then the current session history will overwrite rather than append. The current setting for this option can be displayed using the command: shopt histappend.
This can be seen from the following screenshot:
shopt -u histappend: disables this option
shopt -s histappend: enables this option
We can clear the session history using the history command and the option -c: history -c.
This will not clear the contents of the file, only the current session history; however if histappend is disabled, then exiting from the shell will overwrite the file with the current session history. Of course, if the session history is empty, then, on exit, we will have effectively cleared the history file. But if histappend is enabled, then, on exiting the shell, we will append to the file rather than overwriting it, thus keeping its original content.
Remember:
- shopt -u histappend: then on exit we overwrite the history file
- shopt -s histappend: then on exit we append to the history file
Clearing the bash history file
If we want to be sure we're clearing the history file, even with the histappend option set, we can use the the following commands:
history -c
history -w
While history -c clears the current session history, the -w option overwrites the history file. Clearing the history may be required, as we want to keep the command history protected in the case of root. The logout script for root (/root/.bash_logout) could contain these two lines to ensure no bash history is maintained after logout for root.
We can observe the behavior of clearing the session history:
history -c
history
Here we clear the session history and then display the current session history. This will show the content as nothing more than the execution of history, as all other commands were cleared with history -c. If we now use option -r instead of option -w, what do you think will happen?
history -r
history
This lets us read in the contents of the file to the session history, which is normally what we experience when bash starts. Now the execution of the command history will display the current session history, which includes the contents of the file that was just read into memory.
Controlling duplicate entries in the history
Every time we type the commands ls or cd, they're added to the history file. And while we may want them there, we probably don't need to see them them 70 or 80 times. Setting the environment variable HISTCONTROL to the value of erasedups deletes duplicate commands from the history.
HISTCONTROL=erasedups
This lets us keep the most recent operation of the duplicated command, but deletes previous entries. Avoiding unnecessary duplication also allows us to maintain the effective size of the history file. Add this setting to a user's login script to ensure that it's made each time bash starts. This can be added to the .bashrc file in a user's home directory.
- .bashrc : login script ( there are two login scripts, but use this one for setting this particular variable)
- .bash_logout: logout script
- .bash_history: history file
These files are located in each user's home directory.
Other history-related variables
HISTFILE : Sets the location and name of the history file.
HISTFILESIZE: The variable contains the maximum number of lines that may be in the history file. When bash writes to the history file, the oldest commands that go over this maximum number get deleted.
HISTSIZE: Does the same as above, but controls the maximum lines of current session history to maintain in RAM.
HISTIGNORE: We can set a colon delimited list of commands to never add to history – usually superfluous commands like ls and cd, without arguments and exit. We could add something like this to our login script:
HISTIGNORE=”ls:cd:exit”
This would never add ls to history, but would add ls /etc. The match has to be on the exact command.
Using the bash history
You'll be pleased to know that the history doesn't exist just to consume space in your home directory, but that it's actually useful! Of course, the simplest method involves the aforementioned up arrow key, which lets you go through previously entered commands. But let's move away from that for a moment.
Consider the commands in this screenshot:
- mkdir -p src/java : Creates the src directory with the subdirectory java , the -p option creates parent directories as required.
- mv *.java !$ : Moves all java files into the last argument, in this case src/java.
- cd !$ : Changes are working directly to src/java, again making use of the last argument.
Knowing the fundamentals of the history navigation can help increase the speed of our work at the command line.
Running a command as !v runs the last command starting with v in the session history. The search will start from the most recent command back through the history. Running a command !j runs the last command that started with j. Using this method, we may edit a java source file like this:
vi Test.java
And compile the file with:
javac Test.java
It's very easy to re-edit and recompile in quick successions using
!v
!j
This works very well where we don't need to run commands quickly in succession, and when we're aware of the command history. We can also query the history using history without any options, and then execute a previous line with: !42. This would re-execute line 42 of out session history.
If the text we are searching for in our history doesn't make up the start of the line, then we have to apply a little more guile. Consider a history where we may have pinged several hosts. Using !ping will execute a ping (network discovery) on the last host that we tried.
If, we want to re-execute the ping to the 57 machine, we could use:
!?57
Earlier we also saw that we could interactively search though the history using cntrl + r and typing a string to search. Enter will execute the command, ESC will add the command to the command line ready to edit.
It should be noted that the more we use the history functions, the quicker we'll become (this means practice, practice and more practice).
Online courses
If you want to learn more on this, check out Introduction to the Bash Shell on Mac OS and Linux and my Pluralsight course, Linux Installation and Initial Configuration. You can also get more comfortable with the command line by running through the following exercises.
Exercises
- Connect to the command line of your Raspberry Pi or other Linux system to display the current history.
- Open a second terminal window if using the desktop, and display the history from that window. Type commands and then compare the history in both windows
- Clear the session history from one window.
- Repopulate the history by reading the contents of the history file.
- Now clear the history and the history file.
- Set the variable so that duplicate commands are not stored in the history.
- Set the variable so that ls and cd, when used without options or arguments, are not added to history.
- Make these settings persistent by adding them to the correct user login script.
- What is the name of the bash history file?
- What is the name of the login script we should add the variable to?