All we need is an easy explanation of the problem, so here it is.
I stumbled over this: Boiled down the example is
/ # a=$(eval echo hello world) / # echo $a hello world / # $(eval echo hello world) /bin/sh: hello: not found / # a=$(hello world) /bin/sh: hello: not found
I would expect that the first line expands to
hello world inside the parentheses and is then expanded the same way the last line is:
a=$(hello world). After all
help eval tells us:
Combine ARGs into a single string, use the result as input to the shell, and execute the resulting commands.
If eval does execute the resulting command before expansion of
$() takes place—which I expect—the result should be an error for
hello world is not executable (unless there is an actual
a=$(eval echo hello world) not error out as expected?
How to solve :
I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.
If eval does execute the resulting command before expansion of $() takes place—which I expect—the result should be an error for hello world is not executable (unless there is an actual hello command)
That is not how substitution works. The mere presence of
$(..) is what causes the
eval to even run
To start with,
$(..) Command substitution in bash is a way to capture the output of command run inside parens and storing it in a variable. The
$(..) is replaced by output of contents present inside it
a=$(eval echo hello world)is executed by the shell in following order. The
eval echo hello worldis executed simply as
echo hello worldwhich runs the echo command and outputs the string
hello worldwhich is stored into the variable
$(eval echo hello world)just returns
hello worldas explained in 1). But there is no assignment to capture the output of
$(..)anywhere. So a literal string is returned back to the shell i.e.
hello world. The shell tries to interpret that as a command (the literal
hellostring). Hence the error you see, that a command not found of the name
The same happens for
a=$(hello world)also. As mentioned in 1), the part
$(..)runs the contents inside the parens as commands. So the literal string is evaluated again resulting in the same error.
In cases 2) and 3), when the
$(..) is expanded, the literal unquoted string
hello world was returned back to the shell which was subject to word splitting causing
world as two separate words. Thats why you encounter the error that
hello was run as a command and not the whole string
Be cautious in using
eval with any command, as simple as even
echo. There is a risk of executing dangerous commands when not properly invoked. See Is it always safe to use
Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂