Bash history is a running record of all the commands you type into the bash shell. As you type commands they are stored in RAM and written out to the .bash_history file upon closure of the shell session.

Because all of the commands are stored in this manner, you can manipulate them and use them to increase your command line productivity. During this introductory tutorial, we will examine some of the basics of working with the bash history.

Bash History Variables

There are a few environmental variables that control the configuration of bash history.

HISTFILE - Contains the location of the .bash_history file.
HISTFILESIZE - Contains the maximum number of lines that will be written to the bash history file.
HISTSIZE - Contains the number of events that will be stored in RAM.
HISTCONTROL - Contains rules for what should be written to history and what should be ignored.
HISTTIMEFORMAT - Display a timestamp for each entry in Bash History

HISTCONTROL Settings

The HISTCONTROL variable has four settings that you can manipulate to change it's behavior.

Ignoredups - Do not write line after line duplicate commands.
Ignorespace - Do not write lines that start with one or more spaces to history.
Ignoreboth - Set both above to true.
Undefined - If you do not define any rules, none will be applied.

Check what your current history settings are by using the export command:

[savona@putor ~]$ export | grep -i hist
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"

Change the settings by exporting the variable:

[savona@putor ~]$ export HISTCONTROL="Ignoreboth"
[savona@putor ~]$ export | grep -i hist
declare -x HISTCONTROL="Ignoreboth"
declare -x HISTSIZE="1000"

Make your settings permanent by adding then to your bashrc file.

[savona@putor ~]$ echo "export HISTCONTROL=Ignoredups" >> ~/.bashrc
[savona@putor ~]$ echo "export HISTSIZE=2000" >> ~/.bashrc

Remember to source the file if you want to make the changes take effect without closing your shell.

[savona@putor ~]$ source ~/.bashrc 
[savona@putor ~]$ echo $HISTSIZE
2000

Display The History of Commands

To display a list of previously entered commands on your Linux system, you will use the history command. Simply type history at the prompt like so:

[root@Centos7 ]# history
1  cd /var/log
2  tail -10 /var/log/messages
3  history
... output truncated ...

The history command outputs all of the commands in your history starting with the first and ending with the last. The default value is usually 1000, but this can be controlled by changing the $HISTFILESIZE environmental variable.

Show Last X Number of Commands

You can show the last x number of commands typed by simply following the command with a number. For example if you wanted to see the last 20 commands:

$ history 20
7  vi names
8  for i in `cat names`; do dig +short $i; done
9  vi names
10  for i in `cat names`; do dig +short $i; done
11  for i in `cat names`; do dig +short $i >> ptr; done
12  cat ptr
13  for i in `cat names`; do echo -n "FORWARD: $i - REVERSE: "; dig +short $i; done
14  cd ~
15  cd scripts/
16  ls -lrt
17  vi update-clam.sh
18  freshclam
19  history -d 19
20  cd /var/tmp
21  ls -lrt
22  unzip android-studio-ide-135.1740770-linux.zip
23  cd android-studio/
24  cd bin/
25  ./studio.sh
26  history 20

Calling Commands from History

If you wanted to repeat the very last command, you can simply type !! at the prompt and bash will run the very last command you typed.  These are called "Word Designators". I do not find this command as useful as others because you can just hit the up arrow for the same effect.  But understanding the fact that !! is available, will lead to greater things.  For example, let’s say you wanted to run a command you ran six commands ago.  You can use the just run the following command:

!-6

In my case this ran ls -lrt again, as you can see from my history output, that was six commands up from my last command.

You can also use the ! as a sort of search.  Using my example bash history, let’s say I wanted to run the last for loop I issued.  You could use !-14, but  it is hard to remember how many commands it was ago, at least for me.  Instead we can tell bash to just run the last command starting with “for” like so:

$ !for

Example:

[root@Centos7 ]# !for
for i in `cat names`; do echo -n "FORWARD: $i - REVERSE: "; dig +short $i; done
FORWARD: putorius.net - REVERSE: 50.87.151.78

You can also specify by number, which command you want to run.  For example let’s say I wanted to run command 18 on my example output above.  Using our friend the exclamation point, I can simply issue:

$ !18

Example:

[root@Centos7 ]# !18
freshclam
ClamAV update process started at Sat Mar 21 08:23:52 2015
main.cvd is up to date (version: 55, sigs: 2424225, f-level: 60, builder: neo)
daily.cvd is up to date (version: 20218, sigs: 1354532, f-level: 63, builder: neo)
bytecode.cld is up to date (version: 247, sigs: 41, f-level: 63, builder: dgoddard)

Adding Timestamps to Your Bash History Entries

Using the HISTTIMEFORMAT variable you can show a timestamp in your Bash history for each command you execute. Simply set the variable with your desired format.

[savona@bighat ~]$ HISTTIMEFORMAT="%Y-%m-%d "
[savona@bighat ~]$ history
   25  2015-03-21 speedtest-cli --single
   26  2015-03-21 ssh fenrir
   27  2015-03-21 curl https://ipaddr.pub
   28  2015-03-21 file $(which speedtest-cli)
...OUTPUT TRUNCATED...

You can use the same format specifiers that are used with the date command. Make sure you leave a space at the end of the variable value.

Remember to add it to your .bashrc file to make it permanent.

Using Word Designators in Bash History

There are more complex “Word Designators” also.  These allow you to take a word or words from a previous command and use it again without typing it out. 

Let’s say we wanted to see a files size, then afterwards we decided we wanted to see it’s contents. We can use the word designator “!!:$” to pull the last word from the last command in our bash history and precede it by the cat command like so.

[savona@bighat ~]$ ls -lrt /etc/redhat-release
lrwxrwxrwx. 1 root root 14 May 22 16:39 /etc/redhat-release -> fedora-release
[savona@bighat ~]$ cat !!:$
cat /etc/redhat-release
Fedora release 17 (Beefy Miracle)

The "!!:$" designator can be shortened to "!$"

It is important to know that words are separated by spaces, with the first word starting at 0. 

For example the following command:

dig +short putorius.net

dig is word 0, +short is word 1, and putorius.net is word 2.

Now let’s see an example of how to pull the first word (or 0) from a command in our Bash history.  We will use the same example above, but this time we would like to list the whole directory instead of viewing the contents of a file.

[savona@bighat ~]$ ls -lrt /etc/redhat-release
lrwxrwxrwx. 1 root root 14 May 22 16:39 /etc/redhat-release -> fedora-release
[savona@bighat ~]$ !!:0
ls
Backups   Music     Public      scripts         Documents 
SSLThing      Desktop  Downloads   Pictures  Videos

You can also pull a word from a command using a search syntax. For example, let's say you wanted to use the second word (1) from the last cat command you ran. It doesn't matter if it was the last command you ran or 30 commands back, you can use the "!<string>:1" designator. Here is an example:

[savona@putor ~]$ dig +short ipaddr.pub
104.27.133.158
[savona@putor ~]$ uname -r
4.20.8-200.fc29.x86_64
[savona@putor ~]$ cat /etc/redhat-release
Fedora release 29 (Twenty Nine)
[savona@putor ~]$ host !dig:2
host ipaddr.pub
ipaddr.pub has address 104.27.132.158

As you can see above, "!dig:2" did a reverse search for the most recent command starting with "dig" then took the second word and inserted it into the command.

Searching Bash History

You can also search bash history. At the bash prompt hold down Ctrl and hit r and start typing the first few letters of the command your searching for.  For example, let’s say I was searching for a while loop I typed a little while back, I would hit [Ctrl+r] and type wh, like so:

[root@Centos7 ]#
(reverse-i-search)`wh': while read line; do echo $line | cut -f"s*" -d 2; done < forward

This search will find the last command starting with “wh”, to search higher or deeper into the history simply hit [Ctrl+r] again.

You can also search for a string within a command by issuing exclamation point and wrapping the string in question marks like so:

$ !?FORWARD?

Example:

[root@Centos7 ]# !?FORWARD?
for i in `cat names`; do echo -n "FORWARD: $i - REVERSE: "; dig +short $i; done

You can also use external tools like more, less or grep.

history | more
history | less
history | grep ssh

Using those tools are outside the scope of this tutorial, but feel free to drop my a line if you have specific questions.

Deleting Bash History

Now that we covered viewing, using and searching bash history, let’s talk about clearing or deleting specific items from bash history.

Your bash history is just a text file saved at ~/.bash_history.  You can simply delete that file to clear your bash history, or you can more gracefully issue the history command with the clear option like so:

history -c

You can also delete a specific line from your bash history using the delete option like so:

history -d 12

Print Command Only

This is an important one, I probably should have lead with this. If you have been following along you probably realized that commands are run instantly when using history shortcuts. That is not always a good thing. I have been bitten more than once when I retrieved the wrong result from history and executed something I shouldn't have, somewhere I shouldn't have.

Learn from my mistakes. If you are unsure of what you're about to run from history, use ":p" (print) before running the command. This will print the information it is pulling from history instead of executing it. Then when you're sure that's what you want, you can safely execute it.

[savona@putor ~]$ cd ~/Desktop/TEMP/temp/
[savona@putor temp]$ rm -rf *
[savona@putor temp]$ cd /etc
[savona@putor etc]$ !rm:p
rm -rf *

WOAH! That was close...

Conclusion

There is a lot you can do with Bash history, we are only covering the very basics to get you started. Comment below and let us know your favorite bash history trick.