Command History Shortcuts
The shell lets you bring back old commands and re-enter them, making changes if you want. This is one of the easiest and most efficient ways to cut down on typing, because repeated sequences of commands are very common. For instance, in the following sequence we're going through various directories, listing what's there, deleting files we don't want, and saving certain files under different names:
cd Pictures/ ls -l status.log.* rm status.log.[3-5] mv status.log.1 status.log.bak cd ../Documents/ ls -l status.log* rm status.log.[2-4] mv status.log.1 status.log.bak cd ../Videos/ ls -l status.log* rm status.log.[2-5] mv status.log.1 status.log.bak
Eventually, if you had to do this kind of clean-up regularly, you would write a script to automate it and perhaps use a cron job to run it at regular intervals. But for now, we'll just see how to drastically reduce the amount of typing you need while entering the commands manually.
An earlier chapter showed you how to use arrow keys to move around in your command history as if you were editing a file. This chapter shows a more complicated and older method of manipulating the command history. Sometimes you'll find the methods in this chapter easier, so it's worth practicing them. For instance, suppose you know you entered the mv
command you want (or another one very similar to what you want) an hour ago. Pressing the back arrow repeatedly is a lot more trouble than recalling the command using the technique in this section.
Recalling a command by a string
The bang operator, named after the ! character (an exclamation point, or more colloquially "bang"), allows you to repeat recent commands in your history.
!string executes the most recent command that starts with string. Thus, to execute the exact same mv command you did before, enter:
!mv
What if you don't want the exact same command? What if you want to edit it slightly before executing it? Or just want to look at what the bang operator retrieves to make sure that's the command you want? You can retrieve it without executing it by adding :p
(for "print"):
!mv:p
We'll show you how to edit commands soon.
Perhaps you issued a lot of mv
commands, but you know there's a unique string in the middle of the command you want. Surround the string with ? characters, as follows:
!?log?
Entering two bangs in a row repeats the last run command. A very useful command history idiom is re-running the last command with superuser privilege:
sudo !!as we all happen to type commands without the right permissions from time to time.
While running your last command may seem to have limited use, this method can be modified to select only portions of your last command, as we will see later.
Recalling a command by number
The shell numbers each command as it is executed, in order. If you like recalling commands by number, you should alter your prompt to include the number (a later chapter shows you how). You can also look at a list of commands with their numbers by executing the history
command:
$ history ... 502 cd Pictures/ 503 ls -l status.log* 504 rm status.log.[3-5] 505 mv status.log.1 status.log.bak 506 cd ../Documents/ 507 history $
Here we've shown only the last few lines of output. If you want to re-execute the most recent rm
command (command number 504), you can do so by entering:
!504
But the numbers are probably more useful when you think backwards. For instance, if you remember that you entered the rm
command followed by three more commands, you can re-execute the rm
command through:
!-4
That tells the shell, "start where I am now, count back four commands, and execute the command at that point".
Repeating arguments
You'll often find yourself reusing portions of a previous command, either because you made a typo, or because you are running a sequence of commands for a certain task. We accomplish this using the bang operator with modifiers.
The three most useful modifiers are: *, !^, and !$, which are shortcuts for all, first, and last arguments respectively. Let's look at these in order.
"commandName *" executes the commandName with any arguments you used on your last command. This maybe useful if you make a spelling mistake. For example, if you typed emasc instead of emacs:
emasc /home/fred/mywork.java /tmp/testme.java
That obviously fails. What you can do now is type:
emacs !*
This executes emacs
with the arguments that you last typed on the command-line. It is equivalent to typing:
emacs /home/fred/mywork.java /tmp/testme.java
"commandName !^" repeats the first argument.
emacs /home/fred/mywork.java /tmp/testme.java svn commit !^ # equivalent to: svn commit /home/fred/mywork.java
"commandName !$" repeats the last argument.
mv /home/fred/downloads/sample_screen_config /home/fred/.screenrc emacs !$ # equivalent to: emacs /home/fred/.screenrc
You can use these in conjunction as well. Say you typed:
mv mywork.java mywork.java.backupwhen you really meant to make a copy. You can rectify that by running:
cp mywork.java.backup mywork.java
But since you are reusing the arguments in reverse, a useful shortcut would be:
cp !$ !^
For finer-grained control over arguments, you can use the double bang with the :N
modifier to select the Nth argument. This is most useful when you are running a command with sudo
, since your original command becomes the first argument. The example below demonstrates how to do it.
sudo cp /etc/apache2/sites-available/siteconfig /home/fred/siteconfig.bak echo !^ !!:2 # equivalent to echo cp /etc/apache2/sites-available/siteconfig
A range is also possible with !!:M-N
.
Editing arguments
Often you'll want to re-execute the previous command, but change one string within it. For instance, suppose you run a command on file1:
$ wc file1 443 1578 9800 file1
Now you want to remove file2, which has a name very close to file1. You can use the last parameter of the previous command through "!$", but alter it as follows:
$ rm !$:s/1/2/ rm file2
That looks a little complicated, so let's take apart the argument:
!$ : s/1/2/
The "!$" is followed by a colon and then a "s" command, standing for "substitute". Following that is the string you want to replace (1) and the string you want to put in its place (2) surrounded by slashes. The shell prints the command the way it interprets your input, then executes it.
Because this kind of substitution is so common, you'll be glad to hear there's a much simpler way to rerun a command with a minor change. You can change only one string in the command through this syntax:
$ wc file1 443 1578 9800 file1 $ ^1^2 wc file2
We used a caret (^), the string we wanted to replace, another caret, and the string we want to put in its place.
Searching through the Command History
Use the Ctrl + R key combination to perform a "reverse-i-search". For example, if you wanted to use the command you used the last time you used snort
, start by typing Ctrl + R. In the terminal window you'll see:
(reverse-i-search)`':
As you type each letter (s, n, etc.) the shell displays the most recent command that has that string somewhere. When you finish typing "snort", you can use Ctrl + R repeatedly to search back through all commands containing "snort." When you find the command you're looking for, you can press the right or left arrow keys to place the command on an actual command line so you can edit it, or just press Enter to execute the command.
Sharing Bash History
The Bash shell saves your history so that you can recall commands from earlier sessions. But the history is saved only when you close the terminal. If you happen to be working in two terminals simultaneously, this means you can't share commands.
To fix this--if you want the terminal to save each command immediately after its execution--add the following lines to your ~/.bashrc file:
shopt -s histappend PROMPT_COMMAND='history -a'
Learning these shortcuts can save you a tremendous amount of time so please experiment!