1

In bash, I can evaluate a string as a statement, e.g.

a="echo hello"
eval $a

I recently found that you can also just expand a using

$a

and it seems to work just as well.

Multi-line input does not work, but other than that, is $a generally a legal substitution for eval $a?

jmd_dk
  • 12,125
  • 9
  • 63
  • 94
  • No. Try anything with embedded quotes. – KamilCuk Jul 29 '19 at 10:08
  • In general it is never a good idea to store a command in a string variable. It will fail in so many situations. Consider using a function as: `a() { echo 'hello'; }` – anubhava Jul 29 '19 at 10:12
  • What is your real problem? Please state that with a minimal input and an expected output. It is generally not a recommended approach to use variables to hold commands to run. It gets more complex as more and more compound commands involving decision-making/branching or redirecting, file I/O happens. – Inian Jul 29 '19 at 10:15
  • 2
    Remember `eval` is only 1-vowel away from `evil`. It should be avoided. – David C. Rankin Jul 29 '19 at 10:17
  • @Inian I don't have a problem... – jmd_dk Jul 29 '19 at 10:22
  • @DavidC.Rankin: In `bash` sometimes there's not much choice, really. For example, injecting variables from subprocesses into current environment (e.g. [`eval "$(ssh-agent -s)"`](https://help.github.com/en/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent)) – Amadan Jul 29 '19 at 10:27
  • There are cases where it is appropriate, but they should be considered the exception rather than then rule. Heaven forbid the result of `eval "$(long compound expression)"` inadvertently evaluates to `eval "sudo rm -r /"`. (or some equally nasty surprise) – David C. Rankin Jul 29 '19 at 10:30
  • Oh, for sure. It's just... say, in JavaScript, `eval` is _always_ evil. If you absolutely have to do dynamic code injection, `new Function` is always preferred ("there are cases when it is appropriate"). `eval` is a red flag. In bash, it's merely a yellow one. :p – Amadan Jul 29 '19 at 10:35
  • They are not equivalent, and *neither* is a good idea; see [Bash FAQ 50](https://mywiki.wooledge.org/BashFAQ/050). – chepner Jul 29 '19 at 13:31

3 Answers3

2

See man bash:

The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and pathname expansion.

Anything that happens before variable expansion won't be expanded with plain $a but will be executed with eval "$a" (note the double quotes!)

Example:

$ a='echo {1..5}'
$ eval "$a"
1 2 3 4 5
$ $a
{1..5}

Redirections and pipelines are detected when the command is being parsed, so they won't happen either.

$ a='echo 1+1|bc'
$ eval "$a"
2
$ $a
1+1|bc

Same with variable assignments, command lists, etc.

jmd_dk
  • 12,125
  • 9
  • 63
  • 94
choroba
  • 231,213
  • 25
  • 204
  • 289
  • That's what I thought too, but actually no expansions whatsoever will happen. See my examples... I forgot the pipes tho! – Amadan Jul 29 '19 at 10:28
1

No. First of all, you want to do eval "$a", or what you evaluate could run away from you. But more than that, $a will not do any expansions:

a='echo {a,b}{c,d}'
$a
# => {a,b}{c,d}
eval `$a`
# => ac ad bc bd

a='echo ~'
$a
# => ~
eval "$a"
# => /home/amadan

foo=bar
a='echo $foo'
$a
# => $foo
eval "$a"
# => bar

a='echo $(which echo)'
$a
# => $(which echo)
eval "$a"
# => /usr/bin/echo

a='echo $((1+2))'
$a
# => $((1+2))
eval "$a"
# => 3

a='sort <(echo foo; echo bar)'
$a
# => sort: cannot read: <(echo: No such file or directory
eval "$a"
# => bar
# => foo

a='echo *'
$a
# => echo *.txt
eval "$a"
# => first.txt second.txt

You can use eval to assign variables:

a='baz=quux'
$a
# => -bash: baz=quux: command not found
eval "$a"
# no output
echo "$baz"
# => quux

There may be more that I did not think of.

Amadan
  • 191,408
  • 23
  • 240
  • 301
1

I think a true equivalent of eval "commands and parameters string" would be "${commands_and_parameters_array[@]}"

a=(echo {1..5})
"${a[@]}"

As rightfully commented by @DavidC.Rankin:

Remember eval is only 1-vowel away from evil. It should be avoided.

– David C. Rankin

At, least an array is less evil to some good degree, as it will not mix commands and their parameters.

Léa Gris
  • 17,497
  • 4
  • 32
  • 41