## 30.8. History Substitutions

Although most of the examples here use echo to demonstrate clearly just what is going on, you'll normally use history with other Unix commands.

The exclamation point (`!`) is the default (Section 30.15) history substitution character. This allows you to recall previously entered commands and re-execute them without retyping. To use the `!` in a command line, you have several choices. Some of the following examples are more of a headache than they may be worth, but they are also used to select arguments from the command line in aliases (Section 29.3):

- `!!` repeats the last command.

- `!:` repeats the last command. This form is used if you want to add a modifier (Section 28.5) like the following:

  >     % echo xy
  >     xy
  >     % !:s/xy/yx
  >     echo yx
  >     yx

  The second `!` was left out.

- `!so` repeats the last command that starts with `so`.

- `!?fn?` repeats the last command that has `fn` anywhere in it. The string could be found in an argument or in the command name. This is opposed to `!fn`, in which `!fn` must be in a command name. (The last `?` need not be there. Thus `!?fn` means the same thing.)

- `!34` executes command number 34. You can find the appropriate history number when you list your history using the history command, or by putting the history number in your prompt (Section 4.3).

- `!! &` adds an ampersand (`&`) to the end of the last command, which executes it and places it into the background. You can add anything to the end of a previous command. For example:

  >     % cat -v foo
  >            ...
  >     % !! | more
  >     cat -v foo | more
  >            ...

  In this case the shell will repeat the command to be executed and run it, adding the pipe through the more pager. Another common usage is:

  >     % cat -v foo
  >            ...
  >     % !! > out
  >     cat -v foo > out

  which returns the command but redirects the output into a file.

- `!:0` selects only the command name, rather than the entire command line.

  >     % /usr/bin/grep Ah fn1
  >            ...
  >     % !:0 Bh fn2
  >     /usr/bin/grep Bh fn2

  Note that as an operator (Section 28.5,), `:0` can be appended to these history substitutions as well. For example, `!!:0` will give the last command name, and a colon followed by any number will give the corresponding argument. For example, `!:3` gives the third argument:

  >     % cat fn fn1 fn2
  >            ...
  >     % more !:3
  >     more fn2
  >            ...

- `!:2-4` gives the second through the fourth argument; use any numbers you choose:

  >     % echo 1 2 3 4 5
  >     1 2 3 4 5
  >     % echo !:2-4
  >     echo 2 3 4
  >     2 3 4

- `!:-3` gives zero through the third argument; use any number you wish:

  >     % echo 1 2 3 4
  >     1 2 3 4
  >     % echo !:-3
  >     echo echo 1 2 3
  >     echo 1 2 3

- `!^` gives the first argument of the previous command. This is the same as `!:1`. Remember that just as the `^` (caret) is the beginning-of-line anchor in regular expressions (Section 32.5), `!^` gives the beginning history argument.

  >     % cat fn fn1 fn2
  >            ...
  >     % more !^
  >     more fn
  >            ...

- `!$` gives the last argument of the last command. In the same way that `$` (dollar sign) is the end-of-line anchor in regular expressions, `!$` gives the ending history argument. Thus:

  >     % cat fn
  >            ...
  >     % more !$
  >     more fn
  >            ...

  The new command (`more`) is given the last argument of the previous command. This is also handy for pulling the last argument from earlier commands, which is typically a filename. To get the last argument from the previous vi command, for example, you'd use `!vi:$`. So you could type `lpr !vi:$` to print the last file you edited with vi.

- `!*`is shorthand for the first through the last argument. This is used a lot in aliases:

  >     % echo 1 2 3 4 5
  >     1 2 3 4 5
  >     % echo !*
  >     echo 1 2 3 4 5
  >     1 2 3 4 5

  In an alias:

  >     alias vcat 'cat -v \!* | more'

  This alias will pipe the output of cat -v (Section 12.4) command through more. The backslash (`\`) has to be there to hide the history character, `!`, until the alias is used -- see Section 29.3 for more information.

- `!:2*` gives the second through the last arguments; use any number you wish:

  >     % echo 1 2 3 4 5
  >     1 2 3 4 5
  >     % echo !:2*
  >     echo 2 3 4 5
  >     2 3 4 5

- `!:2-` is like `2*` but the last argument is dropped:

  >     % echo 1 2 3 4 5
  >     1 2 3 4 5
  >     % echo !:2-
  >     echo 2 3 4
  >     2 3 4

- `!?fn?%` gives the first word found that has `fn` in it:

  >     % sort fn1 fn2 fn3
  >            ...
  >     % echo !?fn?%
  >     echo fn1
  >     fn1

  That found the `fn` in `fn1`. You can get wilder with:

  >     % echo 1 2 3 4 5
  >     1 2 3 4 5
  >     % echo !?ec?^
  >     echo 1
  >     1

  That selected the command that had `ec` in it, and the caret (`^`) said to give the first argument of that command. You can also do something like this:

  >     % echo fn fn1 fn2
  >     fn fn1 fn2
  >     % echo !?fn1?^ !$
  >     echo fn fn2
  >     fn fn2

  That cryptic command told the shell to look for a command that had `fn1` in it (`!?fn1?`), and gave the first argument of that command (`^`). Then it gave the last argument (`!$`).

- `^xy^yx` is the shorthand substitution (Section 30.3, Section 30.5) command. In the case of:

  >     % echo xxyyzzxx
  >     xxyyzzxx
  >     % ^xx^ab
  >     echo abyyzzxx
  >     abyyzzxx

  it replaced the first set of characters `xx` with `ab`. This makes editing the previous command much easier.

- `!!:s/xx/ab/` is doing the same thing as the previous example, but it is using the substitute command instead of the `^`. This works for any previous command, as in:

  >     % more afile bfile
  >            ...
  >     % echo xy
  >     xy
  >     % !m:s/b/c/
  >     more afile cfile

  You do not have to use the slashes (`/`); any character can act as a delimiter.

  >     % !!:s:xy:yx

  There we used colons (:), good when the characters you're trying to edit contain a slash. If you want to add more to the replacement, use `&` to "replay it" and then add on whatever you like:

  >     % echo xy
  >     xy
  >     % !!:s/xy/&yx
  >     echo xyyx
  >     xyyx

  The `&` in the replacement part said to give what the search part found, which was the `xy` characters.

  The search part, or left side, cannot include metacharacters (Section 32.3). You must type the actual string you are looking for.

  Also, the example above replaces only the first occurrence of `xy`. To replace them all, use g:

  >     % echo xy xy xy xy
  >     xy xy xy xy
  >     % !!:s/xy/yx/
  >     echo yx xy xy xy
  >     yx xy xy xy
  >     % !!:gs/xy/yx/
  >     echo yx yx yx yx
  >     yx yx yx yx

  The g command in this case meant "do all the `xy` s." And oddly enough, the g has to come before the s command. This may seem odd to those of you familiar with vi, so be careful.

  Or you could have done this:

  >     % echo xy xy xy xy
  >     xy xy xy xy
  >     % !!:s/xy/yx/
  >     echo yx xy xy xy
  >     yx xy xy xy
  >     % !!:g&
  >     echo yx yx yx yx
  >     yx yx yx yx

  In this example, we told the shell to globally (`:g`) replace every matched string from the last command with the last substitution (`&`). Without the g command, the shells would have replaced just one more `xy` with `yx`.

-- DR

Copyright © 2003 O'Reilly & Associates. All rights reserved.