8

I always thought that bash treats backslashes the same when using without or with double quotes, but I was wrong:

[user@linux ~]$ echo "foo \ "
foo \
[user@linux ~]$ echo foo \ # Space after \
foo

So I thought backslashes are always printed, when using double quotes, but:

[user@linux ~]$ echo "foo \" "
foo "
[user@linux ~]$ echo "foo \\ "
foo \

Why is the backslash in the first code line shown?

Motte001
  • 1,220

3 Answers3

11

Section 3.1.2.3 Double Quotes of the GNU Bash manual says:

The backslash retains its special meaning only when followed by one of the following characters: ‘$’, ‘`’, ‘"’, ‘\’, or newline. Within double quotes, backslashes that are followed by one of these characters are removed. Backslashes preceding characters without a special meaning are left unmodified. A double quote may be quoted within double quotes by preceding it with a backslash. If enabled, history expansion will be performed unless an ‘!’ appearing in double quotes is escaped using a backslash. The backslash preceding the ‘!’ is not removed.

Thus \ in double quotes is treated differently both from \ in single quotes and \ outside quotes. It is treated literally except when it is in a position to cause a character to be treated literally that could otherwise have special meaning in double quotes.

Note that sequences like \', \?, and \* are treated literally and the backslash is not removed, because ', ? and * already have no special meaning when enclosed in double quotes.

Eliah Kagan
  • 4,155
6

Backslash is interpreted differently according context:

  • Within double quotes (your first example):

    The backslash  retains its special meaning  only when followed
    by one of the following characters: $, `, ", \, or <newline>.
    
  • Without quotes (your second example):

    A  non-quoted  backslash  (\)  is the  escape  character.   It
    preserves  the  literal  value  of  the  next  character  that
    follows,  with the  exception of  <newline>.  If  a \<newline>
    pair  appears, and  the backslash  is not  itself quoted,  the
    \<newline> is treated  as a line continuation (that  is, it is
    removed from the input stream and effectively ignored).
    
  • Using the construct $'....', where you can use inside the quote the standard backspace character, nearly as in C. e.g. \n, \t, etc.

  • Using backquotes:

    When  the old-style  backquote form  of substitution  is used,
    backslash retains its literal  meaning except when followed by
    $, `, or \.
    

Source of quotes: bash manual

dhag
  • 15,736
  • 4
  • 55
  • 65
  • Some of the text that you quoted has been misinterpreted during the conversion from markdown, resulting in missing words and characters. – dhag Oct 17 '17 at 14:50
  • In this case, I would just quote the manpage as code, avoiding this whole class of issues. (There is some mangling left, I will submit an edit to fix this.) – dhag Oct 17 '17 at 15:14
  • @dhag Although I don't think manpage quotes usually look better as code, I'm glad you've re-edited it, because I now see that I introduced mistakes in my own edit (I turned \<newline> into <newline>) which harmed the post, and which your edit fixes. Sorry about that, and thanks! – Eliah Kagan Oct 17 '17 at 16:15
  • @EliahKagan: No problem. I agree that code isn't the proper way to quote text, but it's sometimes the easiest way to avoid breaking anything :). – dhag Oct 17 '17 at 18:13
0

Here is a 'complicated for the sake of understanding' example. Hopefully it'll be helpful for anyone else trying to understand qutoes and backslashes.

In the example here, I want to repeat the pattern 3 times.

>~# echo 'ABC\n\t'
ABC\n\t

Using single quotes, it is fairly straightforward:

>~# echo 'ABC\n\t' | sed -e 's#ABC\\n\\t#ABC\\n\\tABC\\n\\tABC\\n\\t#'
                                  ↑  ↑      ↑        ↑  ↑     ↑  ↑
                    # These backslashes escaped by sed
ABC\n\tABC\n\tABC\n\t

Here is the way to do it using double quotes: (again, complicated just for the sake of understanding)

>~# echo 'ABC\n\t' | sed -e "s#ABC\\\n\\\t#$(printf '%0.sABC\\\\n\\\\t' $(seq 1 3))#"
                                  ↑   ↑                     ↑ ↑  ↑ ↑
            # These backslashes are removed in double quotes. Showing intermediate stage:

>~# echo 'ABC\n\t' | sed -e "s#ABC\\n\\t#$(printf '%0.sABC\\n\\t' $(seq 1 3))#"

                # Expanding printf results in:

>~# echo 'ABC\n\t' | sed -e 's#ABC\\n\\t#ABC\\n\\tABC\\n\\tABC\\n\\t#'

ABC\n\tABC\n\tABC\n\t

Just to play around and master quotes and backslashes, replace the single quotes around printf with double quotes.