The history in Bash allows programmers to remember commands from the history list in a quick and efficient way. Moreover, one can add, remove or modify commands from history and re-execute it. In this article, I will talk about how to use history expansion in Bash to execute previously executed commands. This way, you will become familiar with the event designator, word designator and modifiers to recall a specific line of command and change it as required.
- Familiarity with word designator and event designator.
- Learning about history expansion technique.
- Modifying and executing commands from the history list.
Working With History Expansion in Bash
History expansion involves selecting commands from the history list which is called event. Choosing specific parts of the selected command is called the words. A colon “:” serves as a separator between the words and the event. Modifiers can be applied to manipulate the chosen words. History expansions are initiated by an exclamation character ‘!‘, in other words, the history expansion character. History expansion follows the conventional Bash quoting system. For instance, the backslash ‘ \ ‘ and the single quote can escape the history expansion character and treat ‘ ! ‘ as a literal.
How to Expand History in Bash Script
History expansion within a bash script can’t access history from the current shell session. To enable history expansion within a bash script, you must utilize the set command with the “-o” option.
Steps to Follow >
❶ At first, launch an Ubuntu Terminal.
❷ Write the following command to open a history.sh file in the built-in nano editor:
➌ Now, write the following script inside the editor:
Script (history.sh) >
!/bin/bash echo “Execute the 47-th command from the history:” set -o history # Enable history expansion set -o histexpand # Execute the first command from the history !47
❹ Use the following command to make the file executable:
❺ Run the script by the following command:
chmod u+x history.sh
When executed, the script recalls and executes the 47th command from the history list, which is “pwd”
17 Useful Examples of History Expansion in Bash
Now, I want to show you a few useful examples of history expansion. These examples will clear your confusion and give you a solid idea about event designator, word designator, history modifiers as well as other concepts related to history expansion.
Example 01: Executing Last Executed Command Using Double Exclamation “!!”
It is often necessary to re-execute the last executed command. Use double exclamation (!!) to re-execute the last command in the terminal.
The last executed command is “date” and double exclamation(!!) re-executed the date command.
An event designator refers to a command from the history list, often relative to the current position. In the above example, the first exclamation triggers history expansion and the second refers to the previous command as an event designator. Here’s a list of event designators as a reference for the next examples.
|!!||Refer to the previous command.|
|!n||Refer to the command line n|
|!-n||Refer to the command n lines from the back|
|!string||Refer to the latest command in the history list that starts with the specified string.|
|!?string[?]||Refer to the most recent command in the history list containing the specified string.|
|^string1^string2^||Quick Substitution. Repeat the last command, replacing string1 with string2. Equivalent to “!!:s^string1^string2^”.|
|!#||The entire command line typed so far.|
Example 02: Executing Command From History List Using “!N” in Bash
If you’ve already executed a command in the terminal, you can re-execute it without typing the command again in the terminal. You can re-execute it by referencing the command’s respective number in the history. For instance, to execute the 9-th command from the history use “!9”.
Here, I executed the 9-th command from the history using “!9” which is “echo $name”.
Example 03: Executing Command From History List Using “!-N”
The “-N” event designator refers to the N-th most recent command in history. To execute the 5-th most recent command from the history use “!-5”
As you can see, the 5-th most recent command in history is “echo $(seq 3)” which generates a sequence of numbers from 1 to 3.
Example 04: Executing Command From History Starting With a Particular String
You can search for commands from the history that start with a particular string. In this example, I will search to execute a command from the history that starts with the word “pw”.
Here, “!pw” searches for any command that starts with the string “pw” from the history. It finds the pwd command that matches the string and immediately executes it.
Example 05: Executing Command From History Containing a Particular String
To recall the most recent command with a particular string, use the “!?string” syntax. Let’s say, I want to get the most recent command that contains the string “cd”. To do so, I type “!?cd” in the terminal.
Example 06: Replacing a String From the Previous Command using “^str1^str2”
One can easily modify part of the last command using the “^” sign and defining the string to find and replace.
echo Hello World ^World^Anita
Here, the first occurrence of the string “World” is replaced by “Anita” in the last command. It also executes the command just after replacement.
Example 07: Getting the First Argument of a Command Using Caret “^” Sign
The caret sign can be used as a word designator to extract and repurpose the first argument. The retrieved argument can be used as an argument of other commands.
echo read.md backup.txt track.xlsx touch !!:^
Here the echo command has three different arguments “read.md”, “backup.md” and “track.xlsx”. I use the first argument of this command with the touch command using the “!!:^” syntax. The first exclamation initiates history expansion. The second exclamation indicates the previous event or the most recent command. The colon separates the event designator from the word designator. Finally, the word designator “^” indicates the first argument. Ultimately, the syntax extract “read.md which is the first argument of the most recent command.
Apart from the “^” sign, there are many other word designators. The following list contains the
word designators that I am going to explain in the later examples of this article.
|0||The 0-th word. For many applications, this is the command word.|
|n||The n-th word or the n-th argument of a command.|
|^||The first argument; that is the word 1.|
|$||The last argument.|
|x-y||A range of words; ‘-y’ abbreviates ‘0-y’.|
|*||All of the words, except the 0-th. In other words, all the arguments of a command.|
|%||The first word is matched by the most recent ‘?string?’ search.|
|x*||Similar to ‘x-$’. It includes all the arguments x-th to the last.|
|x-||Abbreviates ‘x-$’ like ‘x*’, but omits the last word. If ‘x’ is missing, it defaults to 0.|
Example 08: Getting the Last Argument of a Command Using Dollar “$” Sign
Similar to the caret sign the dollar sign “$” recalls the last argument of a command from the history list. Let’s say I want to extract the last argument from the most recent command. “!!:$” syntax can be useful in this context.
echo read.md backup.txt track.xlsx touch !!:$
Here the last executed echo command has three arguments. “Track.xlsx” is the last argument. The “!!:$” syntax extracts this argument and uses it as an argument of the touch command. The dollar “$” sign is the word designator for the last argument.
Example 09: Getting a Particular Argument of a Command from History Using Argument’s Index
One can retrieve a particular argument except the last or first one. It needs to define the index number of that particular argument as a word designator in the history expansion syntax.
echo One two Three echo !!:2
The above image shows that the last executed echo command has three arguments “One”, “Two” and “Three”. Now I want to get the second argument from the last command. I use the index number of the second argument which is 2, as the word designator. So “!!:2” recalls the argument “Two” and reuses it with another echo command.
Example 10: Getting Range of Arguments of Command Using Index Range
Users are not confined to recalling a single argument at a time from the history list. One can use a range of indices to extract a range of arguments.
echo backup1 backup2 backup3 backup4 touch !!:1-3
In the above image, the echo command has four arguments from “backup1” to “backup4”. I use the range “1-3” as a word designator to get the first three arguments.
Example 11: Getting all the Arguments of a Particular Command from History Using Asterisk “*”
It may require getting all the arguments but not the command of an event from the history list. In such a scenario, you can use an asterisk(*) as the event designator to get all the arguments of a particular event.
echo read.md backup.txt track.xlsx touch !!:*
Here, the echo command has three arguments. I want to create separate files using the touch command for each of these arguments. To do this, I use history expansion with an asterisk as a word designator. The asterisk sign retrieves all the words of the referred event.
At this point, you are capable of implementing some mixed types of word designators. I listed those with explanations for your reference.
|2*||Gets all the arguments starting from 2nd argument.|
|2-$||Gets all the arguments starting from the 2nd argument. Same as above.|
|2-||Gets all the arguments starting from 2nd argument (except the last argument).|
Example 12: Removing the Trailing Pathname in Bash
Sometimes, removing the trailing pathname from previous commands can expedite directory navigation or aid in finding essential information. Fortunately, Bash’s history expansion includes a ‘h’ modifier, which removes the trailing pathname of a particular event from the history list.
touch Documents/backup/cloud/file1.txt ls -l !!:$:h
In this example, the touch command creates a file named “file1.txt” in the “Documents/backup/cloud” directory. Now, I want to find information about both the newly created file and other files within the same directory. Instead of retyping the full path, I use “!!:$:h” after the ls command. The “!!” recalls the last command, “:$” captures the last argument, which is the entire path, and ‘:h‘ removes the trailing filename part, “file1.txt”.
History modifiers are not only limited to removing the trailing pathname. There are many history modifiers with different functions. The following list contains all the history modifiers and their functions in history expansion.
|h||Remove a trailing pathname component.|
|t||Remove all leading pathname components, leaving the tail.|
|r||Remove a trailing suffix of the form ‘.suffix’, leaving the basename.|
|e||Remove all but the trailing suffix.|
|p||Print command from history without executing it.|
|q||Quote the substituted words, escaping further substitutions.|
|x||Quote the substituted words as with ‘q’, but break into words at spaces, tabs, and newlines.|
|s||Used in “s/old/new/”. It substitutes new for the first occurrence of old in the event line.|
|&||Repeat the previous substitution.|
|g/a||Cause changes to be applied over the entire event line.|
|G||Apply the following ‘s’ or ‘&’ modifier once to each word in the event.|
Example 13: Removing the Leading Pathname
You can use history modifiers not only to remove the trailing pathname but also to eliminate the leading pathname, effectively leaving the last portion of a path. The “t” history modifier is particularly handy for this purpose.
touch Documents/backup/cloud/file1.txt echo !!:$:t
Consider the context of the last example where I create a file in the “Documents/backup/cloud” directory. Now I want to echo back the filename only that I created instead of moving to that directory. “!!:$:h” syntax does the job. The “!!” recalls the last command, “:$” captures the last argument, which is the entire path and ‘:t‘ removes the leading part which is “Documents/backup/cloud”.
Example 14: Removing the Filename Extension in Bash
At this point, if you’re wondering how to remove a filename extension without deleting the entire leading or trailing pathname, don’t worry. There is a Bash history modifier “r” that can remove any type of filename extension.
touch Documents/backup/cloud/file1.txt echo !!:$:r
In the above demo, “!!:$:r” syntax recalls the last argument of the previous command which is “Documents/backup/cloud/file1.txt”. The “r” modifier is responsible for removing the “.txt” extension.
Example 15: Substituting Using “s/str1/str2/” Syntax in a Command from History List
Unlike the “^str1^str2” structure of editing the last command only, the “s/str1/str2/” syntax can find and replace a string in a command located at any position in the history list.
touch Documents/backup/cloud/file1.txt !!:s/touch/cat
Here, the “!!:s/touch/cat/” command replaces the word “touch” with “cat” in the previous command executed in the terminal.
Sometimes users misspelled words in multiple places in a command and want to replace all those instances. The “g” modifier is useful for this purpose.
cp /etc/password Documents/backup/password.bak !!:s/password/passwd
Here I subconsciously write “password” two times instead of “passwd” when creating a backup of passwd file using the cp command. Then I use “g” modifier to replace all instances of “password” with “passwd”.
Example 16: Repeat Previous Substitution Using “&” Sign
One can repeat the previous substitution using the “&” Sign. In the example below I made the same mistake in spelling the name of passwd file while creating a tar file of it. The “&” modifier repeats the previous substitution which was “s/password/passwd”.
tar cvf password.tar Documents/backup/password.bak !!:g&
Example 17: Printing Command From History List Without Execution
One may want to recall a command from the history list but not the immediate execution of it in the terminal. To stop the execution of commands while expanding the history list, use the “p” modifier.
In the example, I searched for the tar command in the history list with all its arguments using an asterisk as a word designator. I also added the history modifier “p” to stop the immediate execution in case any command matched the search. It shows a matched command but doesn’t execute it.
To conclude, history expansion in Bash is designed to assist users in re-executing previous commands. Easy access to the command history gives the user the power to efficiently reproduce and manage their past interactions with the shell.