Skip to content

Shell history expansion

September 29, 2023
December 8, 2014

History Expansion

http://www.eriwen.com/bash/effective-shorthand/
http://www.catonmat.net/blog/the-definitive-guide-to-bash-command-line-history/
Bash One-Liners Explained, Part IV: Working with history - good coders code, great coders reuse
zshexpn: zsh expansion and substitution - man page
bash: History_Expansion - man page
Bang Bang - Command Recall in Linux
Unix history and bang commands — Jack Hsu

TODO: add zsh manual

man history

Each line of history is a command, consisting of more than one words (0-indexed).

cp myfile.txt my/directory/path
cd !$  # cd my/directory/path

vi /etc/fstabs  # oops!
sudo !!  # sudo vi /etc/fstab

echo 1 2 3 4 5 6 7 8 9 10  # test word designators
/path/to/file/a.b.c        # test modifiers

echo $_  # last argument of last command

Event designators:
! start a history substitution, will use last command if no event designator specified
!! expands to the last command
!42 expands to the 42nd command in the history list
!-3 3 commands before (all arguments)
!string most recent command stating with string
!?string? most recent command stating with string
^foo^bar last command with the first occurrence of foo replaced with bar
!# expands to current command typed so far

Word Designators:
Word designators follow event designators, separated by :

:0 command name
:n n-th word, 0-indexed, of the command (= n-th argument, 1-indexed)
:x-y range of words from x to y (0-indexed, -y is synonymous with 0-y)
:^ 1st word, 0-indexed (= first argument)
:$ last word (= last argument)
:* all words but the zeroth (synonymous with 1-$)
:n* n-th (0-indexed) to last words (synonymous with n-$) (= n-th to last argument)

Shortcuts:
!* !:*
!$ !:$
!^ !:^

Modifiers:
Modifiers follow word designators, separated by :

:h head pathname (dirname)
:t tail pathname (basename)
:e suffix
:r remove suffix
:p print but not execute (TAB in mordern shell does this)
:q/:x quote substituted words to prevent further substitutions
:Q unquote words
:u uppercase all words
:l lowercase all words
:s/foo/bar/ replace foo with bar
:gs/foo/bar/ global replace foo with bar

!!:$:e suffix of the last argument of the last command
!!:$:h basepath of the last argument of the last command

ls /path/to/file/a.b.c
!$:h   # /path/to/file
!$:t   # a.b.c
!$:e   # c
!$:r   # /path/to/file/a.b

echo 1 2 3 4 5 6 7 8 9 10
!:q    #'echo' '1' '2' '3' '4' '5' '6' '7' '8' '9' '10'
!:2-5  # 2 3 4 5
!:*    # 1 2 3 4 5 6 7 8 9 10
!:1-3:s/1/foo/:s/2/bar/:s/3/baz/:q  # 'foo' 'bar' 'baz'