How to manage Bash history

BASH (Bourne Again SHell) is the default shell in practically all Linux-based operating systems. All the commands we write in the terminal are interpreted by the shell, and become part of its history. In this tutorial we see where the shell history is saved, and how to manage it using the “history” built-in command and some environment variables.

In this tutorial you will learn:

  • Where and how BASH history is saved
  • How to visualize the current shell history
  • How to clean the Bash shell history
  • How to force shell history to be written to history file
  • How to modify history behavior via environment variables
How to manage Bash history

Software requirements and conventions used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Distribution-independent
Software The Bash shell
Other None
Conventions # – requires given linux-commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux-commands to be executed as a regular non-privileged user

Where is BASH history kept?

If we open a terminal emulator or we are working in a TTY, and we want to recall a command we run in the past, we usually press the upper arrow key to navigate back, or the down arrow key to navigate forward in the shell history. Have you ever wonder where and how Bash history is saved? The list of commands we invoke in the Bash shell are stored in the so called “history file”, which by default is ~/.bash_profile.

Commands are stored one per line in the file, however, they are initially kept in memory, and written to it only when the shell session is closed, or when we force the writing manually (we will see how to perform such action later in this tutorial).

Working with the “history” built-in command



To visualize the current content of the shell history, we can use the history command, without passing any argument or option. Each line in the output produced by it, is prefixed by number:
$ history
    1  echo "linuxconfig.org"
    2  cat /etc/hostname
    3  ls -l /etc/hostname
    4  history

Cleaning the Bash shell history

The history command is not only used to display the current shell history, but also to perform other operations. To clean all the current history of the Bash shell, for example, all we have to do is to invoke the it with the -c option.

We also have the chance to delete a specific command in the history, by using the -d option, and passing the line offset as argument. Imagine we want to remove line 1 from the current in-memory history. We would invoke the history command as follows:

$ history -d 1

Negative numbers can be used as offset: if we do so, the lines count will start from the end of the list. Passing -1 as offset to the -d option, for example, will cause the last line of the history to be removed. When we perform such operation, however, one thing must be taken into account: the removal will take place after the history -d command is appended to the list itself, so to delete the third line from the end of the history we should actually run history -d -4. As an alternative, we could change our configuration in order to make some commands like history not to be included in the list. We can do so by working with the HISTCONTROL or HISTIGNORE variables: we will see how to do it in the curse of the tutorial.

Writing history to file manually

As we already mentioned, the history of the shell we are currently working in, is written to the history file only when the shell session is closed. What if we want to force in-memory history to be written at a specific time? Once again we can use the history command to perform such action. This time we must invoke it with the -w option, which takes no argument:

$ history -w

Modifying history behavior via environment variables



The behavior of the shell history can be modified by setting the value of some environment variables. Let’s see some examples.

The HISTCONTROL variable

In the previous section we briefly mentioned the HISTCONTROL variable when we talked about excluding certain commands from being included in the shell history. This variable takes a colon-separated “list” of values which influence how commands are included in the history. On most distribution, its default value is simply ignoredups:

$ echo $HISTCONTROL
ignoredups

What does it mean? The ignoredups value makes so that a command is not recorded in the shell history if the previous command in the list is identical to it, so to avoid adjacent duplicates. If we want to avoid duplicates in the whole shell history no matter the position they have, we can use the erasedups value, instead.

Another frequently used value for this variable, is ignorespace, which makes so that commands preceded by a space are not included in the shell history. Let’s update the value of the variable on the fly, and verify this works:

$ HISTCONTROL="ignoredups:ignorespace"
$  echo "this command will not be included in the history"
"this command will not be included in the history"
$  history
    1  HISTCONTROL="ignoredups:ignorespace"

First we changed the value of the HISTCONTROL variable from “ignoredups” to “ignoredups:ignorespace”, in order to use both values (we could simply have used ignoreboth as a shortcut). After changing the variable value, we ran two commands, both preceded by a space. The last one, history, showed the history content. As you can see by taking a look at the output of the command, only the variable assignment was included in the shell history, since the other two were preceded by a space.

Needless to say, the value assigned to the HISTCONTROL variable the way we did above, will be kept only in the current shell session. To make it permanent we should include the assignemnt it in our shell environment file, then logout and login again (or source the file manually).

The HISTIGNORE variable

Just like the HISTCONTROL variable, HISTIGNORE accepts a colon-separated list of elements as value, but it is used to specify what commands should be excluded from the shell history. Let’s see an example: suppose we want to exclude the ls and the history commands from being included in the shell history. Here is the value we would assign to the HISTIGNORE variable:

$ HISTIGNORE="ls:history"
$ echo "this will be included in history"
"this will be included in history"
$ ls
file.txt
$ history
    1  echo "this will be included in history"



As you can observe, after we changed the value assigned to the HISTIGNORE variable, we just echoed a message, then we run the ls and the history commands. From the output of the latter we can see that only the first command was included in the history. One thing should be noticed, however: only the exact command we specify in HISTIGNORE will be excluded. For example, above we set ls to be excluded, however if we run ls -l, the command will be included in the shell history:
$ HISTIGNORE="ls:history"
$ echo "this will be included in history"
"this will be included in history"
$ ls -l
-rw-rw-r--. 1 egdoc egdoc    0 Jan  7 11:51 file.txt
$ history
    1  echo "This ill be included in history"
    2  ls -l

How to avoid this? We can simply use a * (glob) as part of the specified command: it does match every character. We could modify the value of the HISTIGNORE variable, in the following way:

$ HISTIGNORE="ls *:history"
$ echo "this command will be included in history"
this command will be included in history
$ ls -l
-rw-rw-r--. 1 egdoc egdoc    0 Jan  7 11:51 file.txt
$ history
    1 HISTIGNORE="ls *:history"
    2 echo "This command will be included in the shell history"

The HISTSIZE variable

The HISTSIZE variable controls how many commands are kept in the Bash history. By default, at least on the distribution I am using at the moment of writing (Fedora 35), it is set to 1000 by default:

$ echo $HISTSIZE
1000

We can increase or decrease this value and adjust it to our needs. If we exceed the specified value, older commands are deleted from the beginning of the list:

HISTSIZE=3
$ echo "first command"
first command
$ echo "second command"
second command
$ echo "third command"
third command
$ history
    2 echo "first command"
    3 echo "second command"
    4 echo "third command"

As you can see from the lines offset the first command we ran, which was the variable assignment, is deleted from the history list once we run the fourth command. Only three commands are kept. This is an extreme example, but it does hopefully give you an idea.

The HISTTIMEFORMAT variable



The HISTTIMEFORMAT variable can be used to prefix each command in the shell history with a timestamp. The timestamp format is determined by the value assigned to the HISTTIMEFORMAT variable. Some of the notations that can be used are:
Notation Meaning
%d Day
%m Month
%Y Year
%H Hours
%M Minutes
%S Seconds

As always, let’s see an example. Let’s say we want each command in history be prefixed by the date it was launched in, using the YYYY/MM/DD format. Here is the value we would assign to the variable:

$ HISTTIMEFORMAT="%Y/%m/%d %H:%M:%S "
$ echo "this command will be prefixed by a timestamp in shell history"
This command will be prefixed by a timestamp in shell history
$ history
    1  2022/01/07 17:12:09 HISTTIMEFORMAT="%Y/%m/%d %H:%M:%S "
    2  2022/01/07 17:12:12 echo "this command will be prefixed by a timestamp in shell history

Conclusions

In this tutorial we briefly saw how commands we write when using Bash are remembered in the shell history. We saw that history is initially kept in memory, and then stored in the so called “history file”, once the shell session is closed. We saw how to visualize the current shell history, how to clean it, and how to force it to be written to file immediately with the “history” command. We also saw how to modify history behavior via some environment variables.



Comments and Discussions