!
is not a special _variable. (There's a variable called !
, which you can access with $!
, but it's unrelated.) It's a character with a special meaning, depending on where bash sees it and on what comes after.
The !
character starts history expansion. Bash performs history expansion very early when parsing a command line, when it's reading commands interactively (not when it's running a script, even a script sourced with .
or source
from an interactive command line). You can set the variable histchars
to select a different character instead of !
(but most other ASCII characters would conflict with common usage).
The character !
starts a history substitution
except when followed by a space, tab, the end of the line, ‘=’ or ‘(’ (when the extglob shell option is enabled using the shopt builtin).
So things like echo "Hello, world"!
or if ! grep -q foo myfile; …
don't trigger any history expansion.
Also single quotes and dollar-single quotes protect from history expansion (i.e. the history expansion character does not have its special meaning when within '…'
), and a backslash that's quoting a character protects that character from starting a history expansion, but double quotes do not, except (since bash 4.4) when bash is invoked in POSIX compatibility mode. That is, by default,
echo "!foo"
makes !foo
a history reference which is substituted inside the double quotes. You can't simply include an actual exclamation mark in a double-quoted string, because echo "\!foo"
would include the backslash. You have to use something like echo \!"foo"
or echo '!foo'
. I don't know why bash does this: it's a design decision made in the 1980s. (Bash inherits history expansion from csh where it's even worse: even single quotes don't protect from history expansion.)
I can't reproduce this exact case though:
bash-5.0$ echo "Hello, World!"
Hello, World!
But it might depend on the version or configuration of bash. The double quotes themselves don't inhibit history expansion: it's the fact that !"
doesn't fit any of the forms of event designators.
bash-5.0$ echo "Hello!world"
bash: !world: event not found`
set +H
is your friend to turn off history expansion. Personally, I keep that in my.bashrc
everywhere. – Charles Duffy May 13 '23 at 20:08zsh
, notbash
. You can check your shell withecho $0
– GypsyCosmonaut May 14 '23 at 00:22echo $0
to find out: https://askubuntu.com/questions/590899/how-do-i-check-which-shell-i-am-using, https://stackoverflow.com/questions/3327013/how-to-determine-the-current-interactive-shell-that-im-in-command-line – Solomon Ucko May 14 '23 at 02:54