BASH Programming

Bash History Commands and Expansions

In this guide, we will demonstrate how to work with Bash history commands.

Prerequisites:

To perform the steps that are demonstrated in this tutorial, you need the following components:

Bash History

Bash is the default shell in most modern Linux systems. As a successor of “sh”, the original UNIX shell, it comes with numerous features and improvements like the directory manipulation, job control, aliases, command history, and more.

Bash keeps track of all the commands that were previously executed from the terminal. This can be incredibly useful in numerous situations like debugging. It can also reduce the need of typing the same/similar commands over and over.

For history management, Bash comes with two built-in commands:

$ type history

$ type fc

To store the history, Bash uses two different techniques:

  • Whenever working with a shell session, its history is stored in the memory.
  • When closed, the history that is stored in the memory is dumped into a history file.

The default history file that Bash uses is located at:

$ cat ~/.bash_history

There are also a handful of environment variables and keyboard shortcuts that change how Bash handles history.

Working with Bash History

Basic Usage

To get the list of commands that are recently run, use the following command:

$ history

Here, all the commands that are stored in the buffer are listed. Each command has assigned numerical value. The oldest command is assigned with 1.

We can limit the number of commands to print using the following command:

$ history N

Here, N is an integer where N >= 0. The output contains the last N commands from the history.

We can also use the output in tandem with grep for filtering:

$ history | grep <string>

To browse through a long history, we can use the “less” command:

$ history | less

Deleting Commands from History

If you need to remove a specific command from the history, use the following commands:

$ history

$ history -d <command_number>

$ history

Similarly, to remove the commands from M to N from the history, we can use the following commands:

$ history

$ history -d M-N

$ history

To clear the history from the RAM buffer for the current terminal session, use the following commands instead:

$ history -c

$ history

To clear the history from the history file that is stored on the disk, we can overwrite it completely with NULL:

$ cat /dev/null > $HISTFILE

Bash History Settings

There are multiple ways of tweaking how Bash handles history. Many of these options are managed by environment variables.

To change their value, we edit the “bashrc” file:

$ nano ~/.bashrc

After editing, save the file and reload it in Bash.

$ source ~/.bashrc

To make the system-wide changes, edit the “bashrc” which is located at the following locations:

$ nano /etc/bash.bashrc

$ nano /etc/bashrc

Buffer Size

As mentioned earlier, Bash uses two buffers to store the command history in RAM (for the current session) and in a disk file (for all the previous sessions).

The sizes of these buffers are controlled by two environment variables:

  • HISTSIZE: It defines the number of entries to store in the RAM buffer.
  • HISTFILESIZE: It defines the number of entries to store in the disk file.

We can change their value in the “bashrc” to fit our needs:

$ nano ~/.bashrc

For example, to store 5000 entries in both buffers, update “bashrc” with the following code:

$ HISTSIZE=5000

$ HISTFILESIZE=5000

Command Exclusion

By default, Bash stores every single command run in the history buffers. However, we can configure it so that Bash ignores certain commands. It can be useful in situations where you have to run the same commands numerous times, filling the buffer with spam.

  • HISTCONTROL

Let’s start with the following command example:

$ echo "monke" && history 5

$ echo "bonk" && history 5

As the output of the history command demonstrates, only the first echo command is registered but not the second one.

This is the working of the HISTIGNORE environment variable. It tells Bash not to log the commands in the history buffer based on certain patterns. The following values are available:

  • ignoredups: It’s not logged if a command matches the previous history entry.
  • ignorespace: It won’t be logged if a command starts with a space at the beginning.
  • ignoreboth: It applies the rule of both ignoredups and ignorespace.
  • erasedups: All the previous lines that match the current command will be erased from the history.

In the first example, we demonstrated the usage of ignorespace. However, not all distros may ship Bash with this configuration. As always, we can add them to “bashrc”:

$ HISTCONTROL=ignoreboth

It’s also possible to enable multiple options using the following command:

$ HISTCONTROL=ignoredups:ignorespace

Here, ignoredups:ignorespace is the equivalent of ignoreboth.

  • HISTIGNORE

This environment variable can contain one or more patterns. Any command that matches any pattern that is described by the HISTIGNORE will not be registered to either history buffer. The patterns are defined using the regular expressions.

The structure is as follows:

$ HISTIGNORE='<pattern_1>':'<pattern_2>':'<pattern_3>'

For example, to exclude the history and echo commands from the Bash history, update HISTIGNORE as follows:

$ HISTIGNORE='history':'echo *'

We can use the following chain of commands to test it out:

$ ls -l /var/lob &> /dev/null

$ history

$ echo hello world

$ history

Timestamping

Bash can also be configured to log the time that a command was run. It can be useful in various situations like debugging.

To enable the timestamps in Bash history, update the value of HISTTIMEFORMAT:

$ HISTTIMEFORMAT="<format_control_char>"

All the available time format control characters are available in the man page of the date command.

$ man date

The following list includes some simple ones:

  • %T: Time
  • %d: Day
  • %m: Month
  • %y: Year
$ HISTTIMEFORMAT="%T %d: "

History Persistence

When working with the CLI, in many cases, you will find yourself working with multiple terminals. This is where Bash’s history management can become a source of pain.

By default, the history file is updated once the session closes. While it’s fine for a single session, it’s not adequate for multiple simultaneous sessions. We can solve this issue by forcing Bash to update the history file every time a command is run.

To do so, update the value of PROMPT_COMMAND:

$ PROMPT_COMMAND='history -a'

Here, the PROMPT_COMMAND variable can contain valid commands. The contents of PROMPT_COMMAND are run before Bash starts taking the user input. The “history –a” command forces the history to append the contents to the history file.

History Expansion and Designators

Bash comes with a couple of built-in shortcuts to take advantage of its history feature. Here’s the list of the designators:

  • !!: Runs the last command from the history.
  • !N: Runs the Nth command from the history.
  • !-N: Runs the Nth command before the most recent command from the history.
  • !<command>: Runs the most recent <command> command.

The following chain of commands demonstrate their usage:

$ echo 1

$ echo 2

$ echo 3

$ history

$ !echo

$ !-3

$ !1

$ !!

Some designators also work with the command arguments from the history:

  • !:*: Use all the arguments of the most recent command.
  • !:^: Use the first argument of the most recent command.
  • !:N: Use the Nth argument of the most recent command.
  • !:M-N: Use the arguments from M to N of the most recent command.
  • !:$: Use the last argument of the most recent command.

The following chain of commands demonstrate their usage:

$ echo 1 2 3 4 5 6 7

$ echo !:*

$ echo 1 2 3 4 5 6 7

$ echo !:^

$ echo 1 2 3 4 5 6 7

$ echo !:5

$ echo 1 2 3 4 5 6 7

$ echo !:1-5

$ echo 1 2 3 4 5 6 7

$ echo !:$

If you need to work with the parameters of a different command, the designators look like this:

  • !<command>^: Uses the first argument of the <command> command.
  • !<command>$: Uses the last argument of the <command> command.

The following command chain demonstrates their usages:

$ ls -lh /var/log &> /dev/null

$ touch 1.txt 2.txt 3.txt 4.txt 5.txt

$ echo !touch^

$ echo !touch$

History Keyboard Shortcuts

Besides all the commands and environment variables, Bash also supports a handful of keyboard shortcuts for easier history navigation:

  • Up arrow key: Scroll backward
  • Down arrow key: Scroll forward

There are also keyboard shortcuts that are available for an interactive history search:

  • Ctrl + R: Search for commands in history.
  • Ctrl + O: Run the selected command.
  • Ctrl + G: Exit the interactive search.

Conclusion

We discussed about Bash history in details. We learned how Bash stores the command history and how to take advantage of it in different ways. We demonstrated how to work with Bash history using various examples.

Interested in learning more about Bash? The Bash programming sub-category contains hundreds of guides on different features of Bash.

Happy computing!

About the author

Sidratul Muntaha

Student of CSE. I love Linux and playing with tech and gadgets. I use both Ubuntu and Linux Mint.