event designators, word designators and modifiers in bash

Daniel Díaz Carrete
3 min readJan 27, 2020

Nothing groundbreaking or revelatory here, I’m just writing this to better remember some of these bash tricks. You can look at the original documentation here.

Suppose you ls a folder, you like what you see there, and decide to cd into it:

ls /tmp/
cd /tmp/

You had to type the folder twice, though. That sucks.

Bash has syntax that lets you to refer to previously entered commands, and to individual arguments within these commands. It also lets you transform both whole commands and individual arguments in certain ways.

For example, in the previous case, we could have typed

ls /tmp
cd !!:$

The !! part is a event designator. It refers to the previous command. The $ part is a word designator. It refers to the last argument of the command.

This combination is so common that it has a special shorthand, !$:

ls /tmp
cd !$

Another way of referring to the previous command line is !-1:

ls /tmp
cd !-1:$

This becomes more useful when referring to commands other than the last one:

ls /tmp
echo foo
cd !-2:$

Instead of identifying commands — like we have done — by their position in the history, we can refer to them using a search. For example !ls refers to a previous command that starts with ls:

cd /tmp
echo foo
cd !ls:$

!?foo? refers to a command that contains foo anywhere

echo foo bar baz
echo !?foo?:$

More word designators

So far the only word designator we have used is $. We can also use * to refer to all the command arguments minus the command itself:

echo foo bar
echo !!:*

Like with !!:$, there’s a special shorthand, !*:

echo foo bar
echo !*

We can also use explicit positions. 0 is the program name, 1 the first argument…

ls /tmp
echo !!:0

Modifiers

Once we have targeted a previous command, and (optionally) some individual part withing the command, we can apply some simple transformations on it.

For example, h can be used to remove the file name form a path:

ls /tmp/foo.txt
echo !$:h

Meanwhile, t removes the path and leaves the file name:

ls /tmp/foo.txt
echo !$:t

r removes file extensions. Because modifiers stack, you can use it in combination with t:

ls /tmp/foo.txt
echo !$:t:r

s performs substitutions. For example, to change “foo” with “bar” in the last argument:

ls /tmp/foo.txt
echo !$:s/foo/bar/

gs substitutes all occurrences, not just the first one:

echo foo foo foo
echo !*:gs/foo/bar

Remember that, when using modifiers, the use of word designators is optional. If you leave them out, the modifiers work over the whole command:

ls /tmp/foo.txt
!!:s/ls/cat

Substituting over the whole previous command has the special shorthand ^string1^string2^:

ls /tmp/foo.txt
^ls^cat^

(Note that this is equivalent to using s, not gs. It will only change the first occurrence within the whole command.)

More modifiers. q quotes stuff, either individual arguments or the whole command:

ls /tmp/foo.txt
echo !$:q
ls /tmp/foo.txt
echo !!:q

p is a somewhat special modifier that is useful for cautious users. It prints the final command but does not execute it:

ls /tmp/foo.txt
echo !$:s/foo/bar/:p

Helpful history tricks

When locating commands using search (in event designators or when using Ctrl-R) remember that you can use comments # to “tag” commands:

echo foo bar baz #tag
echo whatever
echo !?#tag?:$

If you don’t want a command to be stored in the history, simply precede it with a space (for this to work, the variable HISTCONTROL must be set to ignoreboth).

echo foo bar baz
echo xxx yyy zzz
echo !$ # this targets baz

Powershell excursus

In Powershell, the $$ automatic variable contains the last token in the last line received by the session, while $^ contains the first token in the last line received by the session.

Further resources

Seven God-Like Bash History Shortcuts You Will Actually Use

What does single exclamation mark mean for bash history event designator? Explains why something like !:1 works, instead of requiring !!:1 (but remember that for the $ and * designators there are even more succinct shorthands, as seen previously.)

--

--