hummingV
hummingV

Reputation: 1074

Bash quoting within command substitution

I am having a hard time understanding this. The following command expand files:

% echo "(echo " * " )"
(echo  foo.txt bar.txt  )

But this doesnt

% echo "$(echo " * " )"
 * 

Asterisk * is unquoted. Shouldn't the file globbing happen for both cases? How does command substitution affect this case?

Upvotes: 2

Views: 171

Answers (2)

Charles Duffy
Charles Duffy

Reputation: 295443

It's helpful to look at the commands individually.

% echo "(echo " * " )"

...has only a single quoting context. Here, ( echo is quoted, an * is given unquoted, and then a ) is given quoted.


% echo "$(echo " * " )"

Here, you're performing a command substitution, with the inner command having its own quoting context. Specifically, that inner command is:

# the inner command for the substitution is run first
echo " * "

...which emits * as its output, as one would expect. Notably, because it has its own quoting context, content starts unquoted, and only becomes quoted when the "s are seen.

Then, the substitution is performed; because the $() is inside quotes, that substitution process does not run string-splitting and glob expansion (which, if these steps were followed, would break the output of the inner command into multiple words, and expand each word as a glob).

echo "$(echo "*")"

...thus becomes...

# effectively single-quoted because we already finished running expansions
# so expansions that would be allowed in double-quotes are already finished
echo ' * '

By contrast, if you didn't quote:

echo $(echo "*")

...would become...

echo *

...which would have behavior in line with what you anticipated.


Or, the other way around

echo "$(echo *)"

...would become something akin to

echo 'foo.txt bar.txt'

Upvotes: 3

shellter
shellter

Reputation: 37288

% echo "$(echo " * " )"
# --------|--------|
#produces |        |
#         echo " * "

Which is a quoted *.

Same for the outer echo you have shown, the output of cmd-substitution is quoted, you only see a *.

That is to say, what is inside the $( .... ) is processed before the enclosing command is executed.

IHTH

Upvotes: 2

Related Questions