27

The following fails and I don't understand why:

$ echo "#!" 

the following also fails with the same error message:

$ echo "\#!" 

the error message:

-bash: !": event not found 

Why does it fail? How should the echo be done instead?

2
  • 3
    rici's answer to a duplicate is really stellar if you want a detailed, long exposition. Commented Sep 17, 2015 at 5:50
  • I can't reproduce the error with that code in Bash 5.0. Maybe it's been patched. But I can reproduce it with similar commands, like echo "#!foobar" Commented Aug 4, 2022 at 18:55

5 Answers 5

33

The ! character is used for csh-style history expansion.

If you do not use this feature, set +o histexpand (aka set +H) turns off this behavior. It is turned off for scripts, but often enabled for interactive use. In such cases, my personal recommendation is to turn it off permanently by adding set +o histexpand to your .bash_profile (or .bashrc if you don't have a .bash_profile; this is more complex than I want to try to fit into a parenthetical).

As a workaround, if for some reason you cannot or don't want to turn off and forget about this legacy csh feature, you can use single quotes instead of double quotes -- keeping in mind, of course, their different semantics. If you need to combine quoting with variable interpolation, for example, you can change

echo "#!$SHELL" # oops, history expansion breaks this 

into

 echo '#!'"$SHELL" 

(notice the adjacent single-quoted and double-quoted strings; after the shell is done with this, the quotes will be removed and the string #! will be output next to the value of the variable SHELL with no space between them) or a number of other common workarounds like

 printf '#!%s\n' "$SHELL" 

... though as remarked in a comment, even single quotes don't necessarily prevent this from happening when they are at the end of a pipeline.

Sign up to request clarification or add additional context in comments.

6 Comments

Rolled back the edit with @GaborJozan's answer as that was posted separately anyway, but has the obvious flaw that it adds a space where none was present before.
You can use single instead of double quotes except in some odd circumstances, such as where the single quotes are in the downstream part of a pipeline, as noted in this SO answer. Yes, this can be exasperating sometimes; you could momentarily set +o histexpand if you can't work around it another way.
Thanks for the remark. I added a section emphasizing that there are obviously differences between single and double quotes and included a couple of workarounds.
... or even: echo -e "#\041$SHELL"
Best answer on [bash] "event not found" unfortunely not at 1st place
|
10
echo '#!' 

Basically, with double quotes (") aka "weak quoting", Bash does some wacky things with the string, like variable substitution. With single (') aka "strong quoting" the string is taken literally.

See e.g. here for a more in-depth explanation of quoting.

Comments

9

By default, bash supports csh compatible history-expansion.

In bash

echo #! 

will only print a newline, as # starts a comment.

In

echo "#!" 

the # is part of the string started with ". Such strings are still inspected by bash for special characters. ! is a special character iff it is followed by any other text.

-bash: !": event not found 

In this case, bash expects the !" token to refer to a previous command in shell history starting with ", and does not find one. All by itself, ! will not trigger this behaviour:

$ echo \# ! # ! $ echo fee ! fie fee ! fie 

Finally,

$ echo !echo 

produces two lines, the first line is printed by the shell to show how the pattern above expands to:

echo echo '# !' 

while the second line is just the result of executing the expanded command: echo # !


See Also: The Bash man page on History Expansion

Comments

4

Another workaround may be to put an extra space after the "!" :

# echo "#! " #! 

1 Comment

For years this is how I've worked around the error message (which I never really understood until today) for one-liners like grep -q pattern somefile.txt && echo "YAY! ". And probably still will, because double-quoting strings in the shell is just in my muscle memory.
2

In my case all commands are working. Maybe you can specify your environment.

$ echo "\#\!" \#\! $ echo "#!" echo "#" # $ echo "#!" echo "#" # $ echo $BASH_VERSION 3.2.48(1)-release 

1 Comment

Clearly then you have already disabled histexpand

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.