Bash Command Substitution – Behavior with Command from String in Variable

bashcommand linecommand-substitutionvariable substitution

$ echo $(echo x; echo y)
x y
$ a='echo x; echo y'
$ echo $($a)  # expect 'x y'
x; echo y
  1. Why does command substitution behave in such way?
  2. How to perform the command substitution for list of commands stored in a variable without using eval and bash -c?

echo $(eval $a) and echo $(bash -c "$a") actually does what I want, but I heard that using eval often is a wrong way to solve problems, so I want know how to manage without these commands (using bash -c is in fact just the same thing)

Best Answer

Word splitting happens quite late in the evaluation of a command. Most crucially for you, it happens after variable expansion and command substitution.

This means that the second line in

s="echo a; echo b"
echo $($s)

will first have $s expanded to echo a; echo b and then have that command executed without splitting it into a compound command consisting of two echos.

(details: $s gets split into four words, echo, a;, echo and b. The encapsulating $(...) executes this as one command, echo with three arguments, a;, echo and b.)

What is given to the outmost echo is therefore the string a; echo b (actually three words as $(...) is not in quotes) since this was what was outputted by the innermost echo.

Compare that to

s1="echo a"
s2="echo b"
echo $($s1;$s2)

which results in what you'd expect.

Yes, "eval is evil", most of the time, and sh -c is clunky and as fragile. But if you have a bit of shell code that you trust in a string in a shell script, then these tools are the only (?) way to get that code to execute properly since this often requires having to explicitly evaluate the text in the string as shell code (with all phases of the evaluation from start to finish). Especially if it's a compound command.


I think it's only due to the Unix shell's intimate relation to text that you're lucky enough to have echo $($s) execute something at all.

Just think about the steps you'd have to take to get a C program to execute a piece of C code that it gets given as a string...

Related Question