An Exhaustive Guide to Bash History Expansion

History expansion is a way to recall, modify and execute commands in the history list. Bash shell can remember a single command from the history list at once. Bash attempts to execute the referred command immediately after reading it and before breaking it down into words. 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.

Key Takeaways

  • Familiarity with word designator and event designator.
  • Learning about history expansion technique.
  • Modifying and executing commands from the history list.

Free Downloads

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:

nano history.sh
EXPLANATION
  • nano: Opens a file in the Nano text editor.
  • history.sh: Name of the file.

➌ 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 47-th command from the history
!47
EXPLANATION

The script begins with the shebang, #!/bin/bash, specifying that the script should be executed using the Bash shell. After enabling history expansion with “set -o history” and “set -o histexpand”, the script uses history expansion “!47” to execute the 47-th command from the history list.

❹ Use the following command to make the file executable:

chmod u+x history.sh
EXPLANATION
  • chmod: Changes permissions.
  • u+x: Giving the owner executing permission.
  • history.sh: Name of the script.
Changing execution permission of a Bash scriptRun the script by the following command:

. ./history.sh

Executing a script from the command lineWhen 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.

# Last executed command
date
# Recall the last executed command
!!

History expansion for the last commandThe last executed command is “date” and double exclamation(!!) re-executed the date command.

Event Designators

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.

Syntax Explanation
!! Refer to the previous command.
!n Refer to the command located at n-th line of the history.
!-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”.

!9

Particular command from the history listHere, 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

!-5

History expansion relative to the currentAs 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”.

!pw

Command starts with particular string in history listHere, “!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.

!?cd

Command contains a particular string

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

Replacing argument in the last command using history expansionHere, 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 !!:^

Getting the first argument of a command using history expansionHere 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.

Word Designators

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.

Syntax Explanation
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 !!:$

Getting the last argument of a command using history expansionHere 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 also retrieve a particular argument from the middle like 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

Getting particular argument of a command using history expansionThe 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

Getting range of arguments of a command using history expansion in BashIn 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 !!:*

Getting all the arguments of a command using history expansion in BashHere, 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.

Syntax Explanation
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

Removing trailing pathname using Bash history modifiersIn 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

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.

Syntax Explanation
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

Removing leading pathname using Bash history modifiersConsider 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

Removing filename extension using Bash history modifiersIn 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

Substituting in commands from history listHere, the “!!:s/touch/cat/” command replaces the word “touch” with “cat” in the previous command executed in the terminal.

Global Substitution

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
!!:gs/password/passwd

Global substitution of a command from history listHere 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&

Repeating previous substition

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.

!tar:*:p

Stopping immediate execution of a command from history listIn 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. Output shows a matched command but doesn’t execute it.

Conclusion

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.

People Also Ask

Why history expansion doesn’t work inside bash script?
There may be multiple reasons why history expansion in a shell doesn’t work. Make sure the expansion syntax is unquoted to trigger history expansion. Also, check whether history expansion is enabled or not.
How to disable history expansion in Bash?
To turn off history expansion use the set command. “set -H” effectively disables the history expansion.
How to escape history expansion character in Bash?
You can use backslash as the escape character to use exclamation as literal. 

Related Articles


<< Go Back to An Overview of Shell Expansion in Bash | Bash Scripting Tutorial 

Rate this post
LINUX
FUNDAMENTALS
A Complete Guide for Beginners Enroll Course Now
Md Zahidul Islam Laku

Hey, I'm Zahidul Islam Laku currently working as a Linux Content Developer Executive at SOFTEKO. I completed my graduation from Bangladesh University of Engineering and Technology (BUET). I write articles on a variety of tech topics including Linux. Learning and writing on Linux is nothing but fun as it gives me more power on my machine. What can be more efficient than interacting with the Operating System without Graphical User Interface! Read Full Bio

Leave a Comment