167

Try executing the following under a bash shell echo "Reboot your instance!"

On my installation:

root@domU-12-31-39-04-11-83:/usr/local/bin# bash --version
GNU bash, version 4.1.5(1)-release (i686-pc-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
root@domU-12-31-39-04-11-83:/usr/local/bin# uname -a
Linux domU-12-31-39-04-11-83 2.6.35-22-virtual #35-Ubuntu SMP Sat Oct 16 23:57:40 UTC 2010 i686 GNU/Linux
root@domU-12-31-39-04-11-83:/usr/local/bin# echo "Reboot your instance!"
-bash: !": event not found

Can anyone please explain what is "bash events?" I've never heard this concept before. Also, how should I output "!" at the end of the sentence?

Maxim Veksler
  • 2,555
  • 10
  • 27
  • 32

10 Answers10

144

! is a special character to bash, it is used to refer to previous commands; eg,

!rm

will recall and execute the last command that began with the string "rm", and

!rm:p

will recall but not execute the last command that began with the string "rm". bash is interpreting the exclamation mark in echo "reboot your instance!" as "substitute here the last command that began with the character(s) immediately following the exclamation mark", and grumbles at you that it cannot find an event (command) in your history that began with a single double-quote.

Try

echo reboot your instance\!

to protect (escape) the exclamation mark from bash.

MadHatter
  • 78,442
  • 20
  • 178
  • 229
  • Yes, but should happen if I want to use echo -e "some text $ENV_VAL some more text\nReboot now!" ? – Maxim Veksler Dec 02 '10 at 13:25
  • 1
    Write two echo statements; you get the linefeed for free. – MadHatter Dec 02 '10 at 14:22
  • 2
    Simple solution is to use single quotes and double quotes at once: echo -e "Text\nReeboot"'!' – mmey Oct 02 '14 at 04:51
  • I'm not sure that's simpler, but it will work, yes. – MadHatter Oct 02 '14 at 07:04
  • 10
    This sucks. If I don't escape the exclamation mark, bash throws a fit. If I *do* escape it, the backslash is included in the final string. Why do you do this, Bash!! (http://pastebin.com/g4PYv56A) – Hubro Jan 23 '15 at 07:46
  • 2
    @Hubro in fairness, I'm not sure that's all bash's fault. You have somewhat complicated matters by using ruby to feed things to bash, and it's not clear to me what part ruby is playing in the stripping or reinforcing of escape characters. – MadHatter Jan 23 '15 at 08:25
  • 1
    @MadHatter It ain't Ruby's fault: http://pastebin.com/6ezCP1HS – Hubro Jan 23 '15 at 08:27
  • You may be right; I don't know enough ruby to understand the intricacies of the `<<<` operator. All I do know is that when bash is used unmediated by anything else, the problem admits of a solution; see above. – MadHatter Jan 23 '15 at 08:36
  • @MadHatter : what to do if ! is part of a password that I am trying to enter and getting this error? – Mukul Goel Feb 05 '15 at 16:17
  • It's difficult to answer that within the confines of a comment. I recommend you write a new question, describing in a lot more detail what you're trying to do, showing us what happens, and including a pointer to this question (to show you've tried to do some research). – MadHatter Feb 05 '15 at 16:33
  • @MadHatter Okkie. I will do so. – Mukul Goel Feb 05 '15 at 16:38
  • Thanks! I was struggling using curlftpfs and it faulted because I am having an exclamation mark in the password. Escaping was the solution. – Markus Zeller Nov 14 '16 at 13:55
  • @MarkusZeller thanks for letting me know; I'm glad this answer is still helping people, six years on! – MadHatter Nov 14 '16 at 14:54
  • 3
    `echo 'Reboot your instance!'` works as well, as characters in between `'` makes the shell interpret them literally. – leetbacoon Aug 29 '20 at 23:27
  • After reading this answer, I learned what I needed, and [in this other answer of mine here](https://stackoverflow.com/a/65878993/4561887) I demonstrate 2 different ways to escape the `!` char. – Gabriel Staples Jan 25 '21 at 04:37
  • @leetbacoon As long as you don't need to expand variables in the error string. – Pavel Šimerda Mar 07 '21 at 10:20
  • @PavelŠimerda Right, however if a variable needs expanding, just end the single quotes and begin your variable, e.g. `echo 'Reboot '"${machine_name}"' now!'` – leetbacoon May 15 '21 at 19:36
  • @Hubro `ruby <<< "puts 'Hello, world"'!'"'"` `ruby <<< 'puts "Hello, world!"'` – Jasen May 16 '21 at 22:06
  • @MadHatter: how does ruby enter into this at all? We're just talking about bash and bash's built-in echo command here. :-o – lindes Mar 27 '22 at 23:27
  • @lindes see comment 5 above, by Hubro, who brought it up. – MadHatter Mar 28 '22 at 06:16
98

You can turn off history substitution using set +H.

Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148
  • 3
    Do you know the reasoning behind the syntax? It seems really counter intuitative. You'd think you'd type `+H` to enable something and `-H` to disable something. – Gavin Ward May 16 '16 at 09:19
  • 11
    @PunkyGuy: I don't know the history (pun intended) behind it, but Unix options typically start with a hyphen (or minus) and `+` is simply the opposite of that. – Dennis Williamson May 16 '16 at 13:19
  • 1
    From `help set` (in `bash`): "Using + rather than - causes these flags to be turned off." – Dario Seidl Oct 05 '20 at 08:41
  • 2
    While this question correctly addresses bash, since that's what the question asked, I thought I'd mention that in `zsh`, the approximate equivalent is `set -K` or `setopt NO_BANG_HIST` (capitalization and underscores optional, but that's how it shows up in the docs, so referencing it that way in case folks want further details). – lindes Mar 27 '22 at 23:35
35

To solve your original problem, try using single quotes, rather than double quotes. With the latter, bash will attempt to expand certain characters before passing the result on to the command (echo in this case). With single quotes, bash passes the entire string, unchanged.

! is used in commands to refer to the command line history. See: http://tldp.org/LDP/abs/html/abs-guide.html#HISTCOMMANDS for a full set. With the above example, bash is trying to expand !" as a reference to an event before echo gets a look in, hence the error.

Note that in scripts, all of the history commands are disabled, as they only make sense in an interactive shell.

The only one I use on a regular basis, is !$. It expands to the last argument of the previous command. It's a useful shorthand in places.

SmallClanger
  • 8,947
  • 1
  • 31
  • 45
10

Just put a space between ! and " than it'll work.

lfurini
  • 107
  • 5
Isik Mater
  • 129
  • 1
  • 3
  • 3
    Could you please add an explanation as to why your solution works? – 200_success Sep 26 '14 at 08:20
  • 4
    This works because Bash only treats ! as a special character if it's directly followed by a non-whitespace character. However, it will also add an extra blank to your output, so that might not always be desired... – mmey Oct 02 '14 at 04:50
7

Yes, ! is a special character to bash, it is used to refer to previous commands.

Few of the ways you can handle the situation

The following will output the string as it is

echo 'Reboot your instance!'

The following will execute the command and concatenate the string

echo 'escaping #'" adding `which python` to the string"
echo '#!'`which python`
Hubro
  • 1,098
  • 3
  • 16
  • 35
Syed Qaiser
  • 71
  • 1
  • 1
4

You can also add

histchars=  

In ./bash_profile (or use it as a once-off on the command line)

To disable ! from being a 'special history char', or to set it to something else.

Warning; some things may break when you set it for example to ~ and possibly other strings.

Look:

$ echo "!test"
-bash: !test: event not found
$ histchars=
$ echo "!test"
!test
$ histchars=7
$ echo "7test"
-bash: 7test: event not found
$ histchars=
$ histchars=~
$ histchars=
hars=hars=~     # odd output
$ ~/test
-bash: /test: event not found
2

You could also use a heredoc:

cat <<EOF
Reboot your instance!
EOF

It's annoying in this case because it's not a one-liner but for longer commands (like writing a bash script), it works great.

Tom Saleeba
  • 173
  • 5
1

In bash 4.3 it seems you no longer need to use single quotes or set +H:

$ bash --version
GNU bash, version 4.3.46(1)-release (x86_64-unknown-linux-gnu)
[...]
$ echo "Reboot your instance!"
Reboot your instance!
Eugene Yarmash
  • 2,383
  • 5
  • 32
  • 54
  • Probably a bug, because here, I have 4.4.12 and yet again, I need `set +H` or quoting. Or you have `set +H` somewhere (like in `/etc/profile`). – Bodo Thiesen Apr 02 '18 at 11:20
  • Using !-char not at the end of the sentence verifies bash's event detection is still working fine. Version is 4.3.30 ........ $ echo "Recheck your sentence!foo" bash: !foo: event not found – JohnnyD92 Aug 04 '18 at 14:49
0

!is special character for zsh. It points to previous command.
If you need to use it as string character, try \!

0

escape it: echo reboot your instance\!

And if you ran into something when you need to use two Exclamations, use: ls *[\!'!']*

Adham Amiin
  • 101
  • 1