13

How do echo and printf treat backslashes in zsh, bash and other shells?

Under zsh I get the following behavior:

$ echo "foo\bar\baz"
foaaz
$ echo "foo\\bar\\baz"
foaaz
$ echo 'foo\bar\baz'
foaaz
$ echo 'foo\\bar\\baz'
foo\bar\baz

Under bash, things seem a bit more consistent:

bash$ echo "foo\bar\baz"
foo\bar\baz
bash$ echo 'foo\bar\baz'
foo\bar\baz
bash$

But more concretely: How can I pass a string containing backslashes such as \\foo\bar\something to:

  • echo
  • printf
  • print

and get exactly the same string? (in zsh and bash)?

Here is another experiment with functions in zsh:

function foo
{
    echo -E '$1'
}

$ foo \here\is\some\path $1

How can I have it just print \\here\is\some\path?

Update (Note: this has now been answered in Stephane's comment)

I have tried the following in zsh 5.0.2:

function foo
{
    printf '$s\n' $1 
}

foo '\some\path'

But this prints $s ?

3 Answers3

15

zsh echo behaves the standard way, like bash in UNIX mode. That is it expands \b to the ASCII BS character as the UNIX specification requires.

Don't use echo to display arbitrary strings, use printf:

printf '%s\n' "$1"

print -r -- "$1" also works but is ksh/zsh specific.

echo -E - "$1" work with zsh and I believe some BSDs.

cat << EOF
$1
EOF

works in any Bourne-like shell even those from a few decades when there was no printf command but it spawn a new process, and is really not necessary nowadays as we now have printf everywhere.

And by the way, you need to escape backslashes on a shell command line as it's special to the shell (every shell but rc), so:

$ foo '\\foo\bar'

foo \\foo\bar would pass the "\foo\bar" string to foo which can't reinvent the lost backslash back.

  • Thanks Stephane. Oddly enough, the construction printf '%s\n' "$var" works for me on the command line (zsh 5.0.2, i.e. the latest), but not inside a function (see my update in the OP). Why? – Amelio Vazquez-Reina Mar 29 '13 at 01:09
  • 1
    It's printf '%s\n', not printf '$s\n' you want. Note that you need to quote variables in other shells than zsh ("$1", not $1) and the standard function definition syntax is foo() { ...; }, though any shell but bash allow foo() any-command, and function foo { .... } is the ksh syntax. – Stéphane Chazelas Mar 29 '13 at 08:00
  • @Marcus, that's an answer to a different question. – Stéphane Chazelas Apr 29 '19 at 16:12
  • Why does this not work: printf 'C:\foo\bar.xml'.This works: echo -E 'C:\foo\bar.xml' - Why do you put the dash in echo -E -? – Timo Jan 26 '21 at 18:52
2

new answer: read -r var

-r  raw input - disables interpretion of backslash escapes and line-continuation in the read data

and to display:

printf "%s" "$var"
echo "$var"

should work.

So for your foo function:

function foo
{
    read -r var
    echo -E "var is : ${var}"
}

$ foo 
\\here\is\some\path
var is : \\here\is\some\path

old answer below (not answering, but maybe usefull ^^)

just replace each \ by \\ to tell the shell "that it a literall \ I want". otherwise (for example in zsh, in your example) it could well be that \b means "1 backspace", or whatever.

you could for example use sed:

sed -e 's,\\,\\\\,g' < the_original > the_escaped_backslaches_version

(see how you also need to escape "\" there, to tell sed the same thing: "it is a literall "\" I want) (and notice I surround it by ' ' and not " " to avoid the shell to interpret most of it too)

  • Thanks but as I mentioned in the title: I want to avoid escaping backslashes. – Amelio Vazquez-Reina Mar 28 '13 at 19:00
  • oops, I'll edit ^^ – Olivier Dulac Mar 28 '13 at 19:03
  • Thanks! But read -r var seems to wait for input from STDIN. What if I am passing it as an argument? (e.g. foo \\here\is\some\path) – Amelio Vazquez-Reina Mar 28 '13 at 19:31
  • the shell IS interpreting backslashes. That's why you should probably move the argument passing to a read, see my "foo" example. – Olivier Dulac Mar 28 '13 at 19:33
  • I see, so I take that there is no way to using this using this with the syntax foo \\here\is\some\path in the command line? – Amelio Vazquez-Reina Mar 28 '13 at 19:34
  • unless you change the was the shell you're using interpret things, I guess no (but there may be a way... maybe some different "readline" settings? unfortunately, putting that way as default would break numerous habits (and probably scripts as well)... – Olivier Dulac Mar 29 '13 at 09:44
  • 1
    iow: you want a "non-standard" handling of strings by the shell : you could more easily (and more extensively) have your program be compliant to what you want, instead of trying to have the shell-invoking-that-program to behave like you want. So: pass the args (the ones containing "" that you don't want to escape) once the program is started, via "read -r", and not on its invoking shell command-line. – Olivier Dulac Mar 29 '13 at 10:00
1

Generally speaking you can't, because backslash is the escape character (and as such has to be escaped when its literal value is required). BASH provides single quotes for this purpose, however you can't use escape a single quote within single quoted string.

As for the echo discrepancy, it is a built-in an may behave differently across shells (to some degree). For example the BASH built-in has a -e option which tells it to interpret backslash sequences - with it you'd get the behaviour you saw in the Z shell.

peterph
  • 30,838