242

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?

5
  • 1
    Doing bzr commit -m "It works"! works, too.
    – kba
    Commented May 18, 2013 at 13:59
  • 1
    As I noted before the command you put does actually work on it's own :) bzr commit -m "It works!"
    – h4unt3r
    Commented Aug 18, 2014 at 3:26
  • An even quirkier case with nested quotes: stackoverflow.com/questions/22125658/… Commented Sep 2, 2015 at 18:08
  • 2
    Though the accepted answer is a good workaround for your commit problems, I feel it's not an answer to the actual issue: Bash history expansion. Please consider accepting Dennis' answer?
    – Arjan
    Commented Nov 29, 2015 at 10:19
  • 1
    @h4unt3r Doesn't appear to work for me? echo "It works!" -bash: !": event not found
    – rogerdpack
    Commented Feb 10, 2020 at 18:24

6 Answers 6

210

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!' 
5
  • 31
    Since the question was "How do I escape an exclamation mark?" and not "How do i not expand an exclamation mark?" I do not think this is valid. There are some times (like when passing apostrophes and exclamation marks in the same command-line) that this does not work. The answer below works much better to do what many people need.
    – Jann
    Commented May 24, 2011 at 17:55
  • 12
    @Jann: You are 100% right on this, but I think the issue is here is the one with so many questions on superuser: Do we answer the question to the point, or do we help people solve their specific problem. I think both ways can be useful, and this was looking for help on a specific issue. Commented May 24, 2011 at 18:17
  • touché! This was a specific issue. I just tend to want an answer to my questions to be able to be used in many situations. pS: The reason I even mentioned this was I was needing to pass both an apostrophe and an exclamation mark to a perl program and I needed the below solution. :)
    – Jann
    Commented May 24, 2011 at 18:37
  • 1
    I agree with both Benjamin and Jann but gave a downvote. The title of the question is indexed by search engines and that's how I came here and probably most users come here. That's why I believe it's important that the actual question reflect's the problem behind it. In other forums people are advised (almost forced) to ask precisely what they look for. Answering poorly expressed questions supports sloppiness and that's the exact reason for my downvote here. As a compromise / solution my suggestion is to help adapt the heading so it reflects the actual problem.
    – ChristophK
    Commented Jan 6, 2018 at 10:49
  • 1
    What if I need to use exclamation sign inside a heredoc?
    – Andrei LED
    Commented Apr 22, 2020 at 10:13
194

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.

10
  • 6
    Nice tip, JWD. I didn’t realize bash strings could be concatenated simply by omitting whitespace between them.
    – Alan H.
    Commented Aug 26, 2011 at 21:32
  • Actually if the exclamation mark is at the end of the string you're in the clear echo "Happy birthday!" will work as expected otherwise you can escape it with a backslash, but the backslash will be printed as well XD Bash is not for the faint of heart :)
    – h4unt3r
    Commented Aug 18, 2014 at 3:24
  • @h4unt3r: Strange, that is not my experience. For me, echo "Happy Birthday!" outputs 2 lines. The first is echo "Happy birthday" (no exclamation), and the second is "Happy birthday" (again, no exclamation). Do you have history expansion turned on when you do this test?
    – jwd
    Commented Aug 18, 2014 at 21:05
  • @jwd what version of bash are you running? I always have hist expansion on. Did you accidentally use two !! for your test?
    – h4unt3r
    Commented Aug 19, 2014 at 0:15
  • 1
    THIS should be the answer.
    – ingyhere
    Commented Aug 29, 2022 at 14:33
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

6
  • Thanks--this worked, but I had to do set +o histexpand, not set -o histexpand. Could you edit your answer to fix this?
    – Matthew
    Commented Apr 22, 2010 at 19:04
  • Oops, typo, sorry. Fixed. Commented Apr 22, 2010 at 22:29
  • excelent! was looking for this for AGES.... thx vm! as escape wont work... I dont want to use single quotes too... I will add this to my .bashrc thx!!, never used ! in scripts and for nothing!! it was always troubling... Commented Jul 18, 2013 at 15:19
  • This is so awesome! echo "@AquariusPower!"
    – joehanna
    Commented Jun 30, 2016 at 0:28
  • 1
    how do i retrieve current state for storage, please?
    – wick
    Commented Aug 11, 2021 at 20:18
19

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!'
4

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.

2
  • Hmmm it does not work for me.
    – lzap
    Commented Jun 10, 2014 at 11:52
  • I don't get what you're saying about echoing itself. 'echo -e' and 'printf' interpret escaped octal, hex, and Unicode codes. In cases where you're using either of those, you can use "\041" (the octal for the "!") or "\x21" (the hex) to stand-in for the exclamation point. For example: printf "OMG\041". If you're building a double-quoted string somewhere that isn't echo or printf, like the commit string OP asked about, you can embed $(echo -e "\041") in the string where the exclamation point should be. Like this: bzr commit -m "This is a great commit$(echo -e "\041")". Commented Jan 16, 2019 at 20:16
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)

You must log in to answer this question.

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