Bash history is very powerful. Understanding how to effectively use the bash history expansions will make you extremely productive on the Linux command line.
This article explains 15 examples that uses the following bash history expansion features:
- Event designators – Refers to a particular command in the history. It starts with a !
- Word designators – Refers to a particular word of a history entry. Typically this gets combined with an even designator. Even designators and word designators are separated by a colon
- Modifiers – Modifies the result of the substitution done by the event or word designators
This article is part of our on-going Bash Tutorial Series.
As you already know, to view all the history entries, use the history command. This will display all the commands that were executed earlier along with the number for that command in the history table.
$ history 1 tar cvf etc.tar /etc/ 2 cp /etc/passwd /backup 3 ps -ef | grep http 4 service sshd restart 5 /usr/local/apache2/bin/apachectl restart
Bash History Event Designators
1. Execute a specific command from the history using !n
If you’ve executed a command earlier, instead of re-typing it again, you can quickly execute it by using the corresponding number of the command in the history.
For example, to execute command #4, do the following. This will display command #4 from the history, and execute it immediately.
$ !4 service sshd restart
To execute a command that was typed 2 commands back, do the following.
To execute the previous command, do any one of the following:
$ !! $ !-1
You can also press <Ctrl>-P (if you are in the default emacs mode) to get to the previous command.
If you’ve enabled vi style editing for the command line using ‘set -o vi’, use <Esc>-k to get to the previous command.
2. Execute a command with keywords using !string and !?string
You can also use keywords to execute a command from the history.
The following example will search for previous command that STARTS with the keyword “ps” and execute it. In this example, it picks up the previous command “ps -ef | grep http” and executes it.
$ !ps ps -ef | grep http
The following example will search for previous command that CONTAINS the keyword “apache” and execute it. In this example, it picks up the previous command “/usr/local/apache2/bin/apachectl restart” and executes it.
$ !?apache /usr/local/apache2/bin/apachectl restart
3. Replace a string from the previous command using ^str1^str2^
In the following example, first we executed the ls command to verify a file. Later we realized that we want to view the content of the file. Instead of typing the whole file name again, we can just replace the “ls” in the previous command with “cat” as shown below.
$ ls /etc/cron.daily/logrotate $ ^ls^cat^ cat /etc/cron.daily/logrotate
Note: For additional bash history tips, refer to 15 Examples To Master Linux Command Line History. This explains how to display timestamp in the history, and how to use various history related environment variables including HISTTIMEFORMAT, HISTSIZE, HISTFILE, HISTCONTROL, and HISTIGNORE
Bash History Word Designators
Word designators are very helpful when you want to type a new command, but use the argument from one of the command that was executed earlier. Some of the examples are shown below.
4. Get the 1st argument of a command using :^
In the following example, “!cp:^” was given as an argument to “ls -l” command. “!cp:^” locates the previous command in the history that starts with “cp” and gets the 1st argument of that command.
$ cp /etc/passwd /backup $ ls -l !cp:^ ls -l /etc/passwd
The following example gets the 1st argument from the previous command.
$ ls -l !!:^
5. Get the last argument of a command using :$
In the following example, “!cp:$” was given as an argument to “ls -l” command. “!cp:$” locates the previous command in the history that starts with “cp” and gets the last argument of that command.
$ cp /etc/passwd /backup $ ls -l !cp:$ ls -l /backup
The following example gets the last argument from the previous command.
$ls -l !!:$
6. Get the nth argument of a command using :n
In the following example, “!tar:2” was given as an argument to “ls -l” command. “!tar:2” locates the previous command in the history that starts with “tar” and gets the 2nd argument of that command.
$ tar cvfz /backup/home-dir-backup.tar.gz /home $ ls -l !tar:2 ls -l /backup/home-dir-backup.tar.gz
7. Get all the arguments from a command using :*
In the following example, “!cp:*” was given as an argument to “ls -l” command. “!cp:*” locates the previous command in the history that starts with “cp” and gets all it’s arguments.
$ cp /etc/passwd /backup $ ls -l !cp:* ls -l /etc/passwd /backup
8. Refer to the recently searched word using !%
As we explained above, the “!?apache” will search for the previous history command that CONTAINS the keyword “apache” and execute it.
$ /usr/local/apache2/bin/apachectl restart $ !?apache /usr/local/apache2/bin/apachectl restart
!% will refer to the whole word that was matched by the previous “?” search.
For example, If you’ve searched previously “?apache”, the “!%” will match the whole word “/usr/local/apache2/bin/apachectl”. Note that “/” is treated as part of one word in this context.
So, in this case, by executing the following, you can stop the apache.
$ !% stop /usr/local/apache2/bin/apachectl stop
9. Get range of arguments from a command using x-y
In the following example, “!tar:3-5” was given as an argument to “ls -l” command. “!tar:3-5” locates the previous command in the history that starts with “tar” and gets the arguments from 3 through 5.
$ tar cvf home-dir.tar john jason ramesh rita $ ls -l !tar:3-5 ls -l john jason ramesh
The following gets all the arguments from 2.
$ ls -l !tar:2-$
Please note the following:
- !!:* Gets all the arguments from the previous command
- !!:2* Gets all the arguments starting from 2nd argument.
- !!:2-$ Same as above. Gets all the arguments starting from 2nd argument.
- !!:2- Gets all the arguments starting from 2nd argument (except the last argument).
Bash History Modifers
Modifers are given after the word designators, as explained in the examples below.
10. Remove the trailing path name from a word using :h
In the following example, “!!:$:h” takes the last argument of the previous command, and removes the trailing path name. In this case, it removes the filename, and gets only the full path.
$ ls -l /very/long/path/name/file-name.txt $ ls -l !!:$:h ls -l /very/long/path/name
11. Remove all leading path name from a word using :t
This is exact opposite of the previous example.
In the following example, “!!:$:t” takes the last argument of the previous command, and removes all the leading path names. In this case, it gets only the file name.
$ ls -l /very/long/path/name/file-name.txt $ ls -l !!:$:t ls -l file-name.txt
12. Remove the file name extension from a word using :r
In the following example, “!!:$:r” takes the last argument of the previous command, and removes only the “.suffix” (which is file name extension here). In this case, it removed .txt
$ ls -l /very/long/path/name/file-name.txt $ ls -l !!:$:r ls -l /very/long/path/name/file-name
13. Sed like Substitution in bash history using :s/str1/str2/
Instead of using the “^original^replacement^” as we discussed earlier, we can also use a sed like substitution in the bash history as shown in the example below. This might be easy to remember. !! is to call previous command, “:s/original-string/replacement-string/” is the sed-like syntax to replace a string.
$ !!:s/ls -l/cat/
You can also use the g flag (along with s flag) to do global substitution as shown below. This is helpful when you’ve mistyped multiple word and would like to change all of them together and execute the command again.
In the following example, by mistake I’ve given “password” twice (instead of passwd).
$ cp /etc/password /backup/password.bak
To fix this, just do the following global history sed like substitution.
$ !!:gs/password/passwd/ cp /etc/passwd /backup/passwd.bak
14. Repeat the substitution quickly using :&
If you’ve already executed a bash history substitution successfuly as shown above, you can repeat the same substitution quickly again using :&.
I’ve by mistake typed “password” again instead of “passwd” in another command.
$ tar cvf password.tar /etc/password
Now, instead of retyping the command, or doing the “gs/password/passwd”, I can just use “:&”, which will reuse the last substitution. Use “:g&” for reusing the last subsitution by globally.
$ !!:g& tar cvf passwd.tar /etc/passwd
15. Print the command without executing it using :p
This is very helpful when you are doing complex history substitution, and you want to view the final command before executing it.
In the following example, “!tar:3-:p”, doesn’t really execute the command.
Since we’ve given “:p” here, it just does the substitution and displays the new command. Once you’ve verified the bash history expansion, and if you think this is the command you intended to run, remove the “:p” and execute it again.
$ tar cvf home-dir.tar john jason ramesh rita $ tar cvfz new-file.tar !tar:3-:p tar cvfz new-file.tar john jason ramesh
Comments on this entry are closed.
Very beautiful and usable material
awesome info. thanks for brushing our knowledge
i want to log every command run with any user in /var/log/message.
is that possible
See the 5th point: Get the last argument of a command using :$
$ cp /etc/passwd /backup
$ ls -l !cp:^ —-> it should: ls -l !cp:$ to get the last argument
ls -l /backup
Thanks a ton for sharing such great tips.
Bash is so powerful and those tips are really going to help us a lot.
Good Afternoon Sir,
This article is very fine I am new with linux Plz publish article based on server related as mail server and ftp,squid etc.
Again thanks for Nice Article
I can’t believe! Your tips are the best! Thank you for turning our console life so much joyful.
Very nice tips
Thanks for sharing
Thanks so much :D, nice tips. Thanks for sharing.
Oh you could just vi bindings on the command and save yourself all that pain.
awesome, thanks a lot!
in the daily usage of bash i tend to forget some of the features I use less!
I also learned some new tricks from your post!
great tutorial Thanks
Have a look at the script command. (man script)
I would not log this to /var/log/messages, use separate files.
Thanks for sharing, will have hard time to remember all of those thought
$ls -l !$
works as same as
$ls -l !!:$
Awesome tips, thank you.
Man, I wish I learned all the word designators a long time ago! Thank you!
Personally, I’m afraid to use some of these designators. It looks too easy to make an error and execute something I didn’t intend. But I guess point number #15 (Print the command without executing it using) addresses that.
Don’t forget bash reverse history search ([CTRL]+[R] and enter string, keep pressing [CTRL]+[R] to scroll back) – it’s a bit more safe!
There’s also the [ALT]+[.] shortcut for the last argument. Keep pressing this combination to scroll back 🙂
well laid out material.