I'll attempt a summary of existing answers to similar questions:
!
is expanded by bash
as part of the history expansion feature, which is by default on in interactive shells, but off by default in scripts (non-interactive shells).
- Specifically,
!
together with the following characters is interpreted as an expression that recalls a previous command; e.g., !!
recalls the most recently executed command, and !l
recalls the most recently executed command starting with l
.
An easy way to avoid expansion of !
altogether is to turn history expansion off, which is advisable, given that the feature can be disruptive and given that the readline
library's features, which were introduced later, are a superior replacement.
- To turn history expansion off, add
set +H
(or set +o histexpand
) to your ~/.bashrc
file (or ~/.bash_profile
file on OS X) - this is recommended in the highest-voted answer to "-bash: !": event not found"
- An alternative, though probably ill-advised, is to choose an alternative character for history expansion, by setting the special
histchars
shell variable to the desired character.
With history expansion ON, !
is expanded:
always:
- in unquoted strings:
echo hi!
- in double-quoted strings, irrespective of embedded command substitutions:
echo "hi!"
- Note that this means that
!
is even expanded inside a single-quoted string inside a command substitution embedded in a double-quoted string: apparently, history expansion occurs very early during parsing, before the internals of the double-quoted strings are parsed to recognize embedded command substitutions; e.g.:
echo "$(echo 'hi!')" # '!' is still expanded(!)
- this is the gist of question "How to escape history expansion exclamation mark ! inside a double quoted " command substitution like "$(echo '!b')"?"
- Strictly speaking,
!
parsing doesn't even respect double-quotes per se, and considers any run of non-whitespace characters following !
the history-expansion argument, including "
; for instance, echo foo!"
- despite imbalanced double-quotes - is considered a valid command, as is echo foo!bar"baz
.
never:
- in single-quoted strings:
echo 'hi!'