Bash quotes unescaped on command substitution

3

1

Can someone explain me why this is working (listing the contents of a directory that has a whitespace in its name):

ret="$(ls "my dir")"

Shouldn't it rather be interpreted as:

ret="$(ls "
my dir
")"

As for example with:

ls "my" "dir"

and

ls "my dir"

user2279268

Posted 2015-05-04T21:19:06.670

Reputation: 41

I am asking why ret="$(ls "my dir")" is working. – user2279268 – 2015-05-04T21:42:40.797

I can only guess that bash is "smart" enough to see that a "$(" would be a syntax error ($ inside " is evaluated) and tries to find another match which allows the nesting of " in this case. – Slizzered – 2015-05-04T22:46:29.593

Related: How are double quotation marks in bash matched (paired)?

– Weijun Zhou – 2018-02-04T03:10:32.093

Answers

6

The best guess so far seems to be that you are asking:

The command

    ret="$(ls "my dir")"

is a pretty terrible example of a command that contains four double-quote (") marks.  One might naïvely expect that the second quote would match the first one, and the fourth would match the third, like this:

    ret="$(ls "my dir")"
        ↑-----↑      ↑-↑

as, for example, happens with

    eat "afternoon snack" dinner "midnight snack"

Instead, it “works”, as follows:

    ret="$(ls "my dir")"
        ↑     ↑------↑ ↑
        ↑--------------↑

Why are the nested quotes parsed that way?

This paragraph of bash(1) may explain it:

Command Substitution

                        ⋮
    …  When using the $(command) form, all characters between the parentheses make up the command ….

I guess this means what you have observed, namely, the text between the $( and the ) is treated as if it were a separate command, with its own parsing/pairing of quotes, independent of syntactic elements outside the parentheses.  I concede that, if you aren’t looking for that meaning, it’s very hard to interpret.


But your question, as written, doesn’t make sense.  The whole thing hinges on the very special meaning(s) of $ to the shell.  If we changed that to some other currency symbol,

    ret="£(ls "my dir")"

then it would be treated as two words:

        ret="£(ls "my
(followed by)
                                    dir")"

because, if a non-blank, non-special character appears immediately before an opening quote or immediately after a closing quote, it is included in the same string as the characters enclosed in the quotes.  For example,

    cat Wal"*"mart

looks for a file called Wal*mart, not a file called Wal, one called *, and one called martWal"*"mart is equivalent to Wal\*mart and "Wal*mart".  Similarly, the following example shows four words:

    one" "two  buckle" my "shoe  /don\'t/be/a/" "/cadet  sqrt"(2)"
    ↑-------↑  ↑--------------↑  ↑--------------------↑  ↑-------↑

I don’t know where you got the three-line breakdown that you showed in your question.

G-Man Says 'Reinstate Monica'

Posted 2015-05-04T21:19:06.670

Reputation: 6 509

It's how i would expect it to behave. – user2279268 – 2015-05-05T07:37:11.320