What is the point of eval / bash -c, and not just the evaluation of a variable?

Suppose you have the following command stored in a variable:

COMMAND='echo hello' 

What's the difference between

 $ eval "$COMMAND" hello $ bash -c "$COMMAND" hello $ $COMMAND hello 

? Why is the latest version almost never used if it is shorter and (as far as I can see) does the same?

+8
linux unix bash shell sh
source share
3 answers

The third form is not at all like the other two, but to understand why we need to go into the order of operations with bash when interpreting the command and see which one is executed when each method is used.

Bash Analysis Steps

  • Quotation Processing
  • Inclusion of teams
  • Analysis of special operators
  • Decomposition
  • Word separation
  • Substitution
  • Performance

Using eval "$string"

eval "$string" follows all of the above steps, starting with # 1. Thus:

  • Literal quotes inside a string become syntactic quotes
  • Special operators processed, such as >()
  • Extensions like $foo run
  • The results of these decompositions are divided into characters in spaces for individual words
  • These words expand like globs if they analyze the same and have available matches, and finally, the command is executed.

Using sh -c "$string"

... does the same as eval , but in a new shell running as a separate process; thus, changes in the state of the variable, the current directory, etc. expire when this new process ends. (Note also that this new shell may be a different interpreter supporting a different language, ie sh -c "foo" will not support the same syntax as bash , ksh , zsh , etc.).


Using $string

... begins with step 5, "Word Separation."

What does it mean?

Quotations are not followed.

Thus,

printf '%s\n' "two words" will be parsed as printf %s\n "two words" , unlike the usual / expected behavior of printf %s\n two words (the quotation marks are consumed by the shell).

The division into several commands (on ; s, & s or similar) does not occur.

Thus:

 s='echo foo && echo bar' $s 

... will produce the following output:

 foo && echo bar 

... instead of the following, which would otherwise be expected:

 foo bar 

Special operations and decompositions are not performed.

No $(foo) , no $foo , no <(foo) , etc.

Redirects fail.

>foo or 2>&1 is just another word created by splitting strings, not a shell directive.

+11
source share

There are at least times when they are different. Consider the following:

 $ cmd="echo \$var" $ var=hello $ $cmd $var $ eval $cmd hello $ bash -c "$cmd" $ var=world bash -c "$cmd" world 

which shows the various points at which the variable is being expanded. Even more clearly, if first set -x

 $ set -x $ $cmd + echo '$var' $var $ eval $cmd + eval echo '$var' ++ echo hello hello $ bash -c "$cmd" + bash -c 'echo $var' $ var=world bash -c "$cmd" + var=world + bash -c 'echo $var' world 

We see here a lot of what Charles Duffy says in his excellent answer. For example, an attempt to execute a variable directly prints $var , since the parameter extension and the previous steps have already been completed, so we do not get the value of var , as we do with eval .

The bash -c option inherits only export ed variables from the parent shell, and since I did not export var , it is not available for the new shell.

+1
source share
 $ bash -c "$COMMAND" 

In this version, a new bash interpreter is launched, the command is launched, and then terminated, returning control to the original shell. You don't need to run bash at all to do this, you can run the bash interpreter from tcsh, for example. You can also do this with a bash script to start from a new environment or not pollute the current environment.

EDIT:

As @CharlesDuffy points out, starting a new bash shell will clear the shell variables this way, but environment variables will be inherited by the shell process.

Using eval causes the shell to parse your command twice. In the example you pointed out, executing $ COMMAND directly or executing eval equivalent, but look here to get a better idea of ​​what eval is good (or bad) for.

+1
source share

All Articles