Why does exclamation mark within double quotes cause a Bash error?
Please look at these commands:
$ notify-send SYNC TIME!
$ notify-send 'SYNC TIME!'
$ notify-send "SYNC TIME!"
bash: !": event not found
$
The first two commands produce a notification bubble as expected. The third gives the error shown.
and
$ echo SYNC TIME!
SYNC TIME!
$ echo 'SYNC TIME!'
SYNC TIME!
$ echo "SYNC TIME!"
bash: !": event not found
$
Here as well, the echo
works for first two commands but not in the third.
More problems here (although I was not planning on using this): both notify-send "SYNC!TIME"
and echo "SYNC!TIME"
give bash: !TIME": event not found
.
But both notify-send
and echo
work with "SYNC! TIME"
Can someone please explain why the bash: !": event not found
error appears?
Solution 1:
!
is the default history expansion character in Bash, see the section "HISTORY EXPANSION" in the Bash manpage
-
History expansion doesn't take place if the
!
is enclosed by single quotes, as innotify-send 'SYNC TIME!'
-
History expansion doesn't take place if the
!
is followed by a space, tab, newline, carriage return, or=
, as innotify-send SYNC TIME!
-
History expansion does take place in
echo "SYNC TIME!"
So you'll get an error if there isn't a command starting with
"
in your history
Solution 2:
Because in bash, !
is a reserved word (OK, character), it has special meaning in different contexts. In this particular case, you are falling afoul of its significance in history searching. From man bash
:
History expansions introduce words from the history list into the input
stream, making it easy to repeat commands, insert the arguments to a
previous command into the current input line, or fix errors in previous
commands quickly.
[...]
History expansions are introduced by
the appearance of the history expansion character, which is ! by
default. Only backslash (\) and single quotes can quote the history
expansion character.
Basically, what this means is that bash will take the characters after the !
and search your history for the first command it finds that starts with those characters. It is easier to demonstrate than explain:
$ echo foo
foo
$ !e
echo foo
foo
The !
activated history expansion, which matched the first command starting with e
which was the previously run echo foo
which was then run again. So, when you wrote "SYNC TIME!"
, bash saw the !"
, searched history for a command starting with "
, failed and complained about it. You can get the same error by running, for example !nocommandstartswiththis
.
To print an exclamation mark, you need to escape it in one of these two ways:
echo 'Hello world!'
echo Hello world\!