27

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?

2 Answers 2

35

! 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 in

    notify-send 'SYNC TIME!'
    
  • History expansion doesn't take place if the ! is followed by a space, tab, newline, carriage return, or =, as in

    notify-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

1
  • 4
    This misbehavior can be fixed by adding to your .bashrc the line set +H. Note that ! is already non-special in scripting; treating it as special would break many standards-conforming scripts. It's only treated as "special" in interactive shells, and only by default until you fix it. :-) Mar 16, 2014 at 17:02
15

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\!
4
  • 7
    echo Hello world! does work --- history expansion is not triggered by blanks ;-) (ah, the nice corner cases...)
    – Rmano
    Mar 16, 2014 at 3:43
  • 1
    It's better to rely on escaping the exclamation mark than blanks, though. Mar 16, 2014 at 12:07
  • 1
    @Rmano I prefer to explicitly say so rather than guide me of a behavior that it's possible to change
    – Braiam
    Mar 16, 2014 at 13:23
  • 1
    ! is a reserved word in bash, as POSIX also declares. But I'm pretty sure its status as one is completely unrelated to its role in history expansion and irrelevant to its treatment in double quotes. ! is a reserved word because it negates a command/pipeline's exit status, so it can't be used as a command. No other reserved word is special in a "-quoted context, while $ is not a reserved word but is treated specially. Nov 15, 2017 at 23:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .