shell ! (event designator) cheat sheet

based on “a shell exclamation mark is not for yelling. be lazy.” by filip roséen. applies to interactive bash, csh, tcsh, zsh — not scripts.

anatomy

![event][:word][:modifier]
    |      |        |
    |      |        '--> modifier    [ :h :t :r :e ... ]
    |      '-----------> which part  [ :0 :$ :* :2-3 ... ]
    '------------------> which line  [ !! !-2 !ssh !?needle? ... ]

! followed directly by : refers to the previous line.

event designators — which line

syntaxmeaning
!!previous command
!-2two lines back
!1337line 1337 from history
!sshmost recent line starting with ssh
!?needle?most recent line containing needle
!#the current line, as typed so far
^old^new^rerun previous command with first oldnew

word designators — which part

example previous line: /path/to/script.sh "hello world" --enable 1337

syntaxselectsresult
!:0command word/path/to/script.sh
!:12nd word (1st arg)hello world
!:$last word1337
!:1-2rangehello world --enable
!:*all argshello world --enable 1337
!:1-all args but lasthello world --enable
!:2*3rd word through last--enable 1337

short forms: !$ = !:$, !* = !:*.

modifiers — transform the part

syntaxeffect
:hstrip filename (like dirname)
:tstrip leading path (like basename)
:rstrip extension
:ekeep only extension
:s/a/b/replace first a with b
:gs/a/b/replace all a with b
:pprint, don’t run (e.g. !ssh:p)

the four worth memorizing

syntaxuse case
!$last argument of previous command
!:0rerun that annoyingly located script
!$:hdirectory part of last argument
!$:tfilename part of last argument

everyday combos

$ apt install pkg          # permission denied
$ sudo !!                  # rerun with sudo
 
$ mkdir /var/mnt/cache
$ cd !$                    # cd into it
 
$ touch ~/projects/work/awesome.sh
$ cd !$:h                  # cd to its directory
 
$ ssh 10.240.33.109 -p2222 -i ~/.ssh/prod/ed25519
$ ssh 10.240.33.110 !:2*   # same flags, other host
 
$ ffmpeg -i /rec/day.mov !#:2:r.mkv   # reuse arg from current line, swap ext
$ scp !$:r.* example.com:/media

posix fallback: fc

works in any posix shell (except truly minimal ones like dash / busybox ash). edits history in $FCEDIT (fallback ed; many shells use $EDITOR).

syntaxeffect
fcedit previous command in editor, then run
fc -2edit command 2 back
fc grepedit last command starting with grep
fc -3 -1last three commands in one buffer
fc ssh -1from last ssh line through most recent
fc -s sshrerun last ssh command, no editor
fc -s a=b sshrerun last ssh command with ab
fc -e 'sed -i s/x/y/g' -3 -1rerun last 3 cmds through a non-interactive “editor”

turning it off / customizing

set +H                 # bash: disable history expansion
setopt nobanghist      # zsh: same
histchars='%^#'        # change trigger chars (event, substitution, comment)

common gotcha: echo "!dlrow olleh"event not found (double quotes don’t protect !; single quotes do).