Bash – How to eval two commands

bashquotingshell

If I run this:

echo "echo 'a'; echo 'b'"

It produces a string which I can then run, and it works – echoes a\nb\n.

However, this does not work:

$(echo "echo 'a'; echo 'b'")

Why not? It doesn't work for any command, not just echo. How do I execute a string containing two or more commands?

Best Answer

In $(echo "echo 'a'; echo 'b'"), the shell sees a command substitution. And that's it. So that's going to be a simple command whose arguments are the resulting of the split+glob operator upon the expansion of the command substitution.

The shell will take the output of the echo "echo 'a'; echo 'b'" command, in this case echo 'a'; echo 'b'\n, remove the trailing newline characters so that becomes echo 'a'; echo 'b', split that according the value of the $IFS variable.

If $IFS has not been changed from its default, that will be 4 words: echo, 'a';, echo and 'b'. Each of those words will be subject to globbing. Here, none contain globbing characters, so those words will stay as they are.

So we've got 4 arguments to run a simple command. The first argument will be used to derive the command to run. echo is built-in most shells. So the shell will call its echo builtin with those 4 arguments.

echo ignores its first (0th) argument and outputs the other ones separated by space characters and terminated by a newline character. Some echo implementations expand backslash escape sequences, but there's none here. So echo will output:

'a'; echo 'b'\n

If you want to eval it, that is if you want to have that string interpreted as shell code, use eval:

eval "echo 'a'; echo 'b'"

That's also recognised as a simple command. Because of the quotes, the shell sees two "words": eval and echo 'a'; echo 'b'. Again, those will make up the arguments to the command that is derived from the first.

Here the command is the shell's eval builtin command. Again, eval ignores its first agument. What it does is concatenate its other arguments (here there's only one) with spaces and interpret the resulting string as shell code. When interpreting

echo 'a'; echo 'b'

The shell sees an unquoted ; which delimits commands. The first one is treated as a simple command which ends up calling echo with two arguments... etc.