1. Overview

When we work on the Linux command line, we often need to recall or reference the previously executed command or some parts of that command in the current one.

For example, most of us know that we can press Ctrl-p (or the Up-Arrow key) to recall the last command so that we can modify it and press Enter to execute it.

In this tutorial, we’ll learn some tricks to reuse different parts from the last command in Bash.

All keyboard shortcuts we’ll see in this tutorial are based on Emacs-binding, which is Bash’s default key-binding.

2. Referencing the Last Command in the Current Command

We’ve mentioned pressing Ctrl-p or the Up-Arrow key can bring the last command to the current line. However, sometimes, we want the last command to be a part of the current command.

An example may explain this requirement quickly. Let’s say we want to create a file under the /opt directory. As regular users, we often execute the touch command but forget to add sudo:

kent$ touch /opt/myFile.txt 
touch: cannot touch '/opt/myFile.txt': Permission denied

Then, we realized from the error message above that sudo was missing. So we want to prepend sudo to the previous command.

Of course, we can press Ctrl-p to recall the last command, move the cursor to the right position, and type sudo. Well, “sudo” is short, and moving the cursor (pressing Ctrl-a) to the very beginning isn’t hard to do. However, if we’d like to insert the previous command in the middle of the current complex command, this approach makes us build a complex command by starting from the middle part. Moreover, if we want to reference the last command multiple times in the current command, the Ctrl-p way cannot help us anymore.

Next, let’s see another way to reference the last command in the current line.

2.1. Using !! to Reference the Last Command

We know that we can execute the last command in the command history with the history expansion ‘!!’**,** for example:

$ echo "Linux is awesome!" 
Linux is awesome!
$ !!
echo "Linux is awesome!" 
Linux is awesome!

So, we can use !! in the current command to reference the last command.

Now, let’s solve the previous “sudo” problem in this way:

kent$ touch /opt/myFile.txt 
touch: cannot touch '/opt/myFile.txt': Permission denied
kent$ sudo !!
sudo touch /opt/myFile.txt 
[sudo] password for kent: 
kent$ ls -l /opt/myFile.txt
-rw-r--r-- 1 root root 0 Aug  6 00:41 /opt/myFile.txt

Next, let’s see another example:

$ uptime
 00:50:46 up 7 days,  8:28,  1 user,  load average: 3.20, 3.20, 3.27
$ echo "The output of the cmd: <!!> is: $(!!)"
echo "The output of the cmd: <uptime> is: $(uptime)"
The output of the cmd: <uptime> is:  00:51:05 up 7 days,  8:28,  1 user,  load average: 2.99, 3.15, 3.25

In the example above, first, we test the command uptime to check its output. Then, we want to reference the command twice in a new command.

As we can see, in this case, using !! is pretty handy to solve this problem.

However, we may have a question – while !! references the last command, is it possible to use it if we’d like to make some changes based on the last command?

The answer is “yes”. So next, let’s look at performing history expansion in Bash.

2.2. Performing History Expansion

In Bash, we can perform history expansion by pressing M-^. It’s worth mentioning that, here, ‘M‘ is the Alt key, and ‘^ is Shift-6. So, in other words, pressing Alt-Shift-6 performs history expansion on the current line.

As usual, let’s understand it through a simple example:

kent$ touch /opt/tehFile.txt
touch: cannot touch '/opt/tehFile.txt': Permission denied

As the example shows, we try to create a file called “theFile.txt” under /opt. But, again, we forget to type sudo. Further, we’ve made a typo: “teh“.

Next, let’s see how to reuse the previous command and fix the typo:

Peek-2022-08-06-01-23

As the demo shows, after typing ‘*!!‘, we’ve pressed Alt-Shift-6, then ‘!!*‘ gets expanded so that we can make some changes to the previous command.

3. Referencing the Arguments of the Previous Bash Command

We’ve learned how to reference the entire previous command in the current command. In practice, we often want to reference some parts from the last executed command, such as some arguments. For example, we first edit a file using the Vim editor:

$ vim /a/file/sits/deeply/in/some/directory/file.txt

After we save the changes and exit Vim, we want to copy the file to another directory. So, it would be pretty convenient if we could reference the argument of the previous vim command:

$ cp <reference the file of the vim command> /other/dir

To make the problem more general, let’s execute an echo command with seven arguments:

$ echo one two three four five six seven
one two three four five six seven

Next, in the current command, we would like to reference one or several parameters from the echo command.

3.1. Understanding Word Designators in Bash’s History Expansion

Before we address how to reference desired arguments from the previous command, let’s first understand how Bash’s word designators work.

Bash indexes the previous command’s words as a zero-based array. For example, our echo command is indexed in this way:

echo one two three four five six seven
---- --- --- ----- ---- ---- --- -----
  0   1   2    3    4     5   6    7

As we can see, index 0 indicates the command itself. Then, from the first argument until the last one, we have index 1n.

Bash’s history expansion supports several ways to reference arguments in the previous command. Let’s take our echo command as an example to learn some commonly used expansions:

  • !:^ – referencing the first argument -> “one
  • !:$ – referencing the last argument -> “seven
  • !:n – referencing the n-th argument, for example: !:0 -> “echo”, !:5 -> “five” 
  • !:* – referencing all arguments -> “one two three …. seven
  • !:x-y – referencing arguments in the given range, for instance, !:3-5 -> “three four five

Now that we understand how history expansion works together with the previous commands’ argument indexes, referencing them in the current command isn’t a challenge for us at all.

3.2. Getting Any Parts From the Previous Command

Now, let’s see a few examples of using those history expansions in action. For simplicity, let’s assume the previous command is always “echo one two … seven”.

First, let’s reference the first and the last arguments in a new command:

$ echo "!:^ and !:$"
echo "one and seven"
one and seven

Next, let’s reference from the second until the sixth argument:

$ echo "arg 2nd-6th: !:2-6"
echo "arg 2nd-6th: two three four five six"
arg 2nd-6th: two three four five six

Finally, let’s reference the fourth argument twice and then the second argument:

$ echo "The 4th, 4th, 2nd: !:4 !:4 !:2"
echo "The 4th, 4th, 2nd: four four two"
The 4th, 4th, 2nd: four four two

3.4. Using Keyboard Shortcuts

So far, we’ve seen how to reference different parts from the previous command by history expansion. However, sometimes, we want the history expansions to get expanded so that we can verify if we’ve referenced the expected arguments or make some changes to the arguments.

We’ve learned pressing Alt-Shift-6 can perform history expansions in the current command. This shortcut works for argument references too:

Peek-2022-08-07-00-06

Further, we can use the following shortcuts to recall arguments from the previous command directly without writing history expansions:

  • Alt-. – The last argument
  • Alt-n-. – The n-th argument (When pressing n and the dot key, the Alt key should always be pressed and held.)

Next, let’s see a demo of recalling the last, the fifth, and the first arguments from the previous command:

Peek-2022-08-07-00-19

As the demo shows, when we’ve pressed Alt-5, Bash prompts (arg: 5) at the beginning of the line. Then if we press the dot key with the Alt key held, “five” is inserted into the cursor position.

4. Conclusion

In this article, we’ve explored using Bash’s history expansion to reference the previous command entirely or only the desired arguments.

Moreover, we’ve learned keyboard shortcuts to perform history expansions or recall any arguments of the last command in the current command.

With these techniques in mind, we can work with the command line more productively.