From the Bash FAQ:
Backslashes (\) inside backticks are handled in a non-obvious manner:
$ echo "`echo \\a`" "$(echo \\a)" a \a $ echo "`echo \\\\a`" "$(echo \\\\a)" \a \\a
But the FAQ does not break down the parsing rules that lead to this difference. The only relevant quote from man bash
I found was:
When the old-style backquote form of substitution is used, backslash retains its literal meaning except when followed by $, `, or .
The "$(echo \\a)"
and "$(echo \\\\a)"
cases are easy enough: Backslash, the escape character, is escaping itself into a literal backlash. Thus every instance of \\
becomes \
in the output. But I'm struggling to understand the analogous logic for the backtick cases. What is the underlying rule and how does the observed output follow from it?
Finally, a related question... If you don't quote the backticks, you get a "no match" error:
$ echo `echo \\\\a`
-bash: no match: \a
What's happening in this case?
update
Re: my main question, I have a theory for a set of rules that explains all the behavior, but still don't see how it follows from any of the documented rules in bash. Here are my proposed rules....
Inside backticks, a backslash in front of a character simply returns that character. Ie, a single backslash has no effect. And this is true for all characters, except backlash itself and backticks. In the case of backslash itself, \\
becomes an escaping backslash. It will escape its next character.
Let's see how this plays out in an example:
a=xx
echo "`echo $a`" # prints the value of $a
echo "`echo \$a`" # single backslash has no effect: equivalent to above
echo "`echo \\$a`" # escaping backslash make $ literal
prints:
xx
xx
$a
Let's analyze the original examples from this perspective:
echo "`echo \\a`"
Here the \\
produces an escaping backslash, but when we "escape" a
we just get back a
, so it prints a
.
echo "`echo \\\\a`"
Here the first pair \\
produces an escaping backslash which is applied to \
, producing a literal backslash. That is, the first 3 \\\
become a single literal \
in the output. The remaining \a
just produces a
. Final result is \a
.
GNU bash, version 5.0.2(1)-release (x86_64-apple-darwin16.7.0)
– Filefailglob
set and for some reason bash is trying to treat\a
as a filename pattern. I have no idea why it would do this. – Enslaveecho `echo \\\a`
also outputs\a
. Weird stuff. Now I understand better why so many say to use$(...)
instead! – Retrenchment\\a
as output! The first 4 give you a literal backslash, and as before you need at least three more before you get a second literal backslash. – Retrenchmentshopt -s failglob; echo $(<<<"\a" cat)
is wrongly parsed. Why does filename expansion triggers there? @Retrenchment , the behavior also ahppens with$(
, ex.shopt -s failglob; echo $(echo "\\a")
I getno match
error. This looks like a bug. filename expansion shouldn't happen on\a
string, only when the argument after word splitting has*
or?
or[
– Immixture\a
. – Retrenchmentdocker run -ti --rm bash:4.4.12 -c 'shopt -s failglob; echo $(<<<"\a" cat)'
works, butdocker run -ti --rm bash:5 -c 'shopt -s failglob; echo $(<<<"\a" cat)'
fails. Looks like a bug. At least it inconsistent between versions. 4.4.23 looks like the last working version. – Immixture