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
| syntax | meaning |
|---|---|
!! | previous command |
!-2 | two lines back |
!1337 | line 1337 from history |
!ssh | most 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 old → new |
word designators — which part
example previous line: /path/to/script.sh "hello world" --enable 1337
| syntax | selects | result |
|---|---|---|
!:0 | command word | /path/to/script.sh |
!:1 | 2nd word (1st arg) | hello world |
!:$ | last word | 1337 |
!:1-2 | range | hello world --enable |
!:* | all args | hello world --enable 1337 |
!:1- | all args but last | hello world --enable |
!:2* | 3rd word through last | --enable 1337 |
short forms: !$ = !:$, !* = !:*.
modifiers — transform the part
| syntax | effect |
|---|---|
:h | strip filename (like dirname) |
:t | strip leading path (like basename) |
:r | strip extension |
:e | keep only extension |
:s/a/b/ | replace first a with b |
:gs/a/b/ | replace all a with b |
:p | print, don’t run (e.g. !ssh:p) |
the four worth memorizing
| syntax | use case |
|---|---|
!$ | last argument of previous command |
!:0 | rerun that annoyingly located script |
!$:h | directory part of last argument |
!$:t | filename 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:/mediaposix 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).
| syntax | effect |
|---|---|
fc | edit previous command in editor, then run |
fc -2 | edit command 2 back |
fc grep | edit last command starting with grep |
fc -3 -1 | last three commands in one buffer |
fc ssh -1 | from last ssh line through most recent |
fc -s ssh | rerun last ssh command, no editor |
fc -s a=b ssh | rerun last ssh command with a → b |
fc -e 'sed -i s/x/y/g' -3 -1 | rerun 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).