244

I want to do something like bzr commit -m "It works!". I can sort of escape the exclamation mark by doing bzr commit -m "It works\!". However, then my commit message includes the backslash. How do I escape the exclamation mark, while still ignoring the backslash?

Matthew
  • 15,036

6 Answers6

212

Since you do not depend on bash to expand variables in your commit message you could use single quotes instead. Strings in single quotes are not expanded by bash.

bzr commit -m 'This does work!' 
200

Here is another method if you want double quotes as well as the exclamation:

echo "It's broken"'!'

This works even if the ! is not at the end of the line.

For instance:

echo "hello there"'!'" and goodbye"

Bonus: A similar technique can be used to escape any text in Sh or Bash (with the help of sed): see the first option in this answer. Further, if you have bash-completion installed, you likely have the quote() function available already.

jwd
  • 3,735
91

Turn off history expansion:

set +H

or

set +o histexpand

You can add one of those commands to your ~/.bashrc if you usually don't use history expansion.

Bash 4.3 added a special case:

the history expansion character is also treated as quoted if it immediately precedes the closing double quote in a double-quoted string

20

Use single quotes (') instead of double quotes ("). Single quotes turn off all interpretation of the stuff in them, while double quotes only turn off some.

bzr commit -m 'It works!'
KeithB
  • 10,386
5

I just now found another way, that will at least work with echoing strings (sentences) you want to punctuate with an exclamation point. It does an end-run, more or less, around Bash histexpand and takes only a bit longer to code.

The hex for an exclamation point, as listed on http://www.ascii-code.com/, is 21, so if you put \x21 at the end of your string, echo -e $foo, make $foo its own expanded echo [ie, foo=$(echo -e "$foo")], what you get when you echo $foo again is the string with an ! at the end. And no switching histexpand either.

Works for sure in Bash 4+. Earlier versions, ymmv.

3

I found another way to do this, recently.

This is a general way to get Bash to do escaping for you, so it's handy for arbitrary cases.

From this article:

Enter a line of Bash starting with a # comment, then run !:q on the next line to see > what that would be with proper Bash escaping applied.

bash-3.2$ # This string 'has single' "and double" quotes and a $
bash-3.2$ !:q
'# This string '\''has single'\'' "and double" quotes and a $'
bash: # This string 'has single' "and double" quotes and a $: command not found

You can also add the p modifier (so: !:q:p to avoid the 'command not found' error)

jwd
  • 3,735