Let's start with a simple command, which works either way, but for different reasons.
$ cmd="echo anymessage"
$ eval '$cmd'
eval '$cmd'
has eval
processing the string $cmd
. It splits it into words, which is again simply the single word $cmd
. It expands the parameter to echo anymessage
and performs one more round of word-splitting to get echo
and anymessage
. Now, echo
is the command and anymessage
is the argument, and the echo
command is executed.
$ eval "$cmd"
Here, eval
is processing the string echo anymessage
, because bash
has performed parameter expansion on $cmd
before executing eval
. The string is again split into words, with echo
recognized as the command.
Now, let's use a more complicated value for cmd
.
$ cmd="cat /data/test/test.tx* | grep toto"
$ eval '$cmd'
Again, eval
receives the literal string $cmd
. That's the entire command line, after word-splitting, so it's a simple command: no pipeline, no redirections, etc. The parameter is expanded to the single string cat /data/test/test.tx* | grep toto
. Next, word splitting produces the words cat
, /data/test/test.tx*
, |
, grep
, and toto
. Pathname expansion occurs
on the pattern; let's just say it expands to the single file name /data/test/test.txt
. You now have 5 words with no more expansions to perform, so the shell identifies the word cat
in command position, and runs that with the remaining 4 words as arguments. The error occurs when cat
cannot find files named |
, grep
, and toto
.
Finally, let's use double quotes
$ eval "$cmd"
This time, eval
receives the expanded string cat /data/test/test.tx* | grep toto
as the command line. After word splitting, it sees cat
, /data/test/test.tx*
, |
, grep
, and toto
. This multi-word command line contains a pipe character, so eval
processes it as a pipeline. Compare to the previous example, where eval
's single argument only contained a single word $cmd
. From here, you should be able to see how the pipeline is executed as expected.
In summary, you need to use double-quotes on the argument to eval
if it is a single parameter containing a complex command line that you want to parse and execute.