1. Overview
Whenever we work on any Linux terminal, more often than not, we use the Bash shell. We type many common commands multiple times with different options. Sometimes while typing a verbose command, we realize that there is some mistake. Instead of retyping the whole text, we use rich history features to correct and search.
In this tutorial, we’ll see how to use Bash history to work more efficiently.
2. Basic Configuration to Enable History
For the Bash shell to allow the history operation, we first need to enable history. We also need to configure the Readline library to read the input from the command line. In this section, we’ll discuss how to enable history and configure the Readline library.
When the Bash shell starts, it initializes history and reads the .inputrc file for the Readline library configuration.
2.1. Enabling Bash History
First, let’s check if history is enabled or not:
$ set -o | grep history
history on
In case history is turned off, we’ll turn it on by setting in the .bashrc file:
$ set -o history
After that, let’s find out whether the file location and history size are set or not by using the echo command. The HISTFILE variable stores the location of the history file:
$ echo $HISTFILE
If the value is /dev/null, then as always, we need to set it up in our .bashrc file:
HISTFILE=$HOME/.bash_history
We use the HISTSIZE and HISTFILESIZE variables to configure the length of the Bash history:
$ echo $HISTSIZE
$ echo $HISTFILESIZE
If the value of either HISTSIZE or HISTFILESIZE is zero, we need to configure them in the .bashrc file:
HISTFILESIZE=500
HISTSIZE=500
2.2. Configuring the Readline Library
Now, we’ll configure the Readline Library. For this purpose, we need to work with the .inputrc file. The .inputrc file describes key bindings, variable assignment, and conditional syntax. We can override the default behavior of the Readline library by customizing the .inputrc file in the home directory.
For changing the editor from emacs (default mode) to vi:
$ set editing-mode vi
To set the visible bell:
$ set bell-style visible
In case we want to ignore the case:
$ set completion-ignore-case On
After setting the variables, let’s move on to configuring keys for traversal.
For ANSI mode, we’ll set arrow keys as:
"\M-[D": backward-char
"\M-[C": forward-char
"\M-[A": previous-history
"\M-[B": next-history
We can get the same behavior by using the bind command in the .bashrc file:
$ bind '"\e[A": history-search-backward'
$ bind '"\e[B": history-search-forward'
Here, \M- is the meta key. If the meta key is not there, it implies the escape key, which is \e.
3. Using Bash History
Now that the shell is configured, let’s start using the history. First, we’ll see how to search the existing history file. Then, we’ll learn how to use the built-in commands that Bash provides. And, in the end, we’ll see how to modify and execute the commands.
3.1. Traversing the History
We use arrow keys or Control keys to traverse the history:
- ⬆️ or CTRL-p: Traverses backward
- ⬇️ or CTRL-n: Traverses forward
3.2. Searching the Commands in History
We can search command history in either incremental or non-incremental mode. For incremental mode, the search begins as soon as we start typing the search string:
- CTRL- r: searches in the backward direction
- CTRL- s: searches in the forward direction
3.3. Bash Built-in Commands
The Bash shell comes with the built-in fc and history commands.
We use the fc command mainly to list, edit, and re-execute a portion of the history list, and we use the history command to manipulate the history list.
First, let’s look at the fc command.
To list the commands used on the terminal:
$ fc -l
39 vi file1
40 ls -la file3
41 ls file3
42 cd ..
43 history
44 ls -la file3
45 ls
46 cd -
47 ls
48 rm file*
49 touch file1 file2 file3 file4
50 ls file3
51 history
As we can see, we got the listing of history commands.
In case we want to edit the commands before executing, we use fc with different options. All the fc commands below open in an editor.
In case the last command is:
$ cat incorrectfilename.demo
Then, after executing the command:
$ fc
The editor will contain command or text:
$ cat incorrectfilename.demo
And, to edit the command at the nth location, say 4th from the end of the listing below:
$ fc -4
43 history
44 ls -la file3
45 ls
46 cd -
47 ls
48 rm file*
49 touch file1 file2 file3 file4
50 ls file3
51 history
52 tou
53 fc -l
54 ls file3
In this case, the editor will contain the command or text:
$ history
To edit commands in a range, we use:
$ fc 49 50
The editor will open with the following lines:
$ touch file1 file2 file3 file4
$ ls file3
If needed, we can change commands in the editor. After exiting the editor, the modified command is executed.
Now, let’s take a look at the history command.
This command lists all the executed commands from the history file along with line numbers:
$ history
33 ls
34 ls
35 pwd
36 pwd
37 set -o history
39 pwd
In case we want to see the last n executed commands:
$ history 3
38 set -o history
39 pwd
40 history 3
We can execute the last command by using:
- ⬆️
- !!
- !-1
- CTRL-P
To clear the history:
$ history -c
As expected, if we execute the history command now, only one command is listed.
$ history
32 history
To delete a specific command from the history:
$ history -d 32
It will delete event number 32 from the history file.
3.4. Using Simple History Expansion
Other than built-in commands, history has a rich set of features. We use the expansion character ‘!’ on the lines/commands in history. Over here, the command selected from the history is called an event, and words are the part of the event on which we act. We also apply modifiers to manipulate the selected words.
In case we want to execute an event, say the command at line 34 from the history:
!34
To run an event starting from specific letters, say last used command starting from p:
!p
$ pwd
3.5. More Advanced History Commands
Sometimes, we want to execute a different command but using the same value or parameter. We’ll use the word designator “!!:$” for this purpose.
As we can see below, the cd command takes the argument Demo from the last event, ‘ls Demo’:
$ ls Demo
file1 file2
$ cd !!:$
$ cd Demo
If we want to apply the arguments of the last event on the current command, then we use the expansion character ‘!’ followed by ‘^’, ‘$’, or “:n”.
Here, rm takes the first argument of event touch and removes file1. However, to remove file4, we’ll use the character ‘$’. Similarly, we’ll use “:2” to remove file2:
$ touch file1 file2 file3 file4
$ rm !^
$ rm file1
Another way we can manipulate an event is by using modifiers. Here, “!to:3” expands to the last event starting with the text “to”, which is the touch command in this case. After that, it substitutes the 3rd argument of the event as an argument for the ls command:
touch file1 file2 file3 file4
$ ls !to:3
$ ls file3
file3
3.6. Showing the Date and Time of Each Command in History
By default, most Linux distributions don’t show the date and time when each command was executed. We can modify this behavior by setting the HISTTIMEFORMAT variable either temporarily or permanently.
Let’s first see how we can set it temporarily:
$ export HISTTIMEFORMAT='%Y-%m-%d %H:%M:%S '
We’re exporting the HISTTIMEFORMAT variable and setting it to the standard date-time format.
%Y-%m-%d represents the date format starting with the year, month, and then, day. %H:%M:%S represents the hour, minute, and second. This method is very useful because it gives us more control of what to display.
We can simplify this format by modifying the command:
$ export HISTTIMEFORMAT='%F %T '
Here, %F expands to the standard date format, and %T expands to the standard time format.
Let’s now run the history command:
$ history
33 2022-05-03 17:58:33 ls
34 2022-05-04 12:04:44 ls
35 2022-05-04 12:04:57 pwd
36 2022-05-04 12:05:44 export HISTTIMEFORMAT='%F %T '
37 2022-05-04 12:05:44 history
We can see that each command is preceded by the date and time it was executed.
Now, let’s see how we can set the HISTTIMEFORMAT variable permanently.
We first need to open the ~/.bashrc file:
$ nano ~/.bashrc
Then, we add this line:
export HISTTIMEFORMAT='%F %T '
Finally, we need to run the source command to update the change we made:
$ source ~/.bashrc
Now, every time we run the history command we’ll also get the date and time that each command was executed.
4. Conclusion
In this article, we discussed how the Readline and the History libraries help in Bash history expansions. We also saw how using these expansions can increase our work efficiency. We can always refer to the following references for more details: