tomsmeding tomsmeding - 4 months ago 10
Bash Question

sh-3.2 double vs quadruple backslash

I'm on OS X 10.11.5, and I have a

/bin/bash
on my system with version 3.2.57. The
/bin/sh
reports the same thing, suggesting that
/bin/sh
is really bash 3.2.

My
/bin/sh
exhibits some weird behaviour:
echo \\
gives a single backslash (as expected), but
echo \\\\
also gives
\
as output, and you need 6 backslashes to get two as output. Demonstration:

>> /bin/bash
~$ echo \\
\
~$ echo \\\\
\\
~$ echo \\\\\\
\\\
~$ exit

>> /bin/bash --posix
~$ echo \\
\
~$ echo \\\\
\\
~$ echo \\\\\\
\\\
~$ exit

>> /bin/sh
sh-3.2$ echo \\
\
sh-3.2$ echo \\\\
\
sh-3.2$ echo \\\\\\
\\
sh-3.2$ exit

>> /bin/sh --posix
sh-3.2$ echo \\
\
sh-3.2$ echo \\\\
\
sh-3.2$ echo \\\\\\
\\
sh-3.2$ exit

>> /bin/sh --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)
Copyright (C) 2007 Free Software Foundation, Inc.

>> /bin/bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)
Copyright (C) 2007 Free Software Foundation, Inc.


Question: why doesn't
echo \\\\
give two backslashes as output in
/bin/sh
, while it does in
/bin/bash
?

More info:

sh-3.2$ echo \\
\
sh-3.2$ echo \\\\
\
sh-3.2$ echo \\\\\\
\\
sh-3.2$ echo \\\\\\\\
\\
sh-3.2$ echo \\\\\\\\\\
\\\
sh-3.2$ echo \\\\\\\\\\\\
\\\

Answer

You are seeing the difference between the POSIX and bash implementations of echo. In short, POSIX echo expands certain escaped characters, while bash does not treat a backslash specially unless you use the -e option. Also, bash uses the POSIX implementation when it runs as sh, or when you explicitly set the xpg_echo option to use the POSIX version.

# POSIX
$ echo 'a\nb'
a
b
# bash
$ echo 'a\nb'
a\nb
$ echo -e 'a\nb'
a
b

Single quotes are just a shorthand for escaping every character contained with in. Using explicit escaping instead of single quotes for all of the above:

# POSIX
$ echo \a\\\n\b
a
b
# bash
$ echo \a\\\n\b
a\nb
$ echo -e \a\\\n\b
a
b

Double quotes are similar to single quotes, except the shell first processes a very limited set of escaped characters first. This is because $ is treated specially inside double quotes, indicating a parameter expansion. You can escape the $ to prevent this, and you can escape a backslash to prevent it from escaping the following character. In all other cases, the backslash is treated literally and passed along to echo.

$ foo=5
$ echo "$foo"
5
$ echo "\$foo"
$foo
$ echo "\\$foo"
\5
$ echo "\\\$foo"
\$foo
$ echo "\t"
\t

With that out of the way, how does each shell create echo \\\\? First, both shells perform quote removal to eliminate quotations marks and escaping backslashes. The first backslash escapes the second, and the third escapes the fourth, leaving just a pair of literal backslashes as the argument to echo. The POSIX version of echo treats \\ as a literal backslash, while the bash version treats \\ as a pair of literal backslashes.