The third form is not at all like the other two -- but to understand why, we need to go into the order of operations when bash in interpreting a command, and look at which of those are followed when each method is in use.
Bash Parsing Stages
- Quote Processing
- Splitting Into Commands
- Special Operator Parsing
- Expansions
- Word Splitting
- Globbing
- Execution
Using eval "$string"
eval "$string"
follows all the above steps starting from #1. Thus:
- Literal quotes within the string become syntactic quotes
- Special operators such as
>()
are processed
- Expansions such as
$foo
are honored
- Results of those expansions are split on characters into whitespace into separate words
- Those words are expanded as globs if they parse as same and have available matches, and finally the command is executed.
Using sh -c "$string"
...performs the same as eval
does, but in a new shell launched as a separate process; thus, changes to variable state, current directory, etc. will expire when this new process exits. (Note, too, that that new shell may be a different interpreter supporting a different language; ie. sh -c "foo"
will not support the same syntax that bash
, ksh
, zsh
, etc. do).
Using $string
...starts at step 5, "Word Splitting".
What does this mean?
Quotes are not honored.
printf '%s
' "two words"
will thus parse as printf
%s
"two
words"
, as opposed to the usual/expected behavior of printf
%s
two words
(with the quotes being consumed by the shell).
Splitting into multiple commands (on ;
s, &
s, or similar) does not take place.
Thus:
s='echo foo && echo bar'
$s
...will emit the following output:
foo && echo bar
...instead of the following, which would otherwise be expected:
foo
bar
Special operators and expansions are not honored.
No $(foo)
, no $foo
, no <(foo)
, etc.
Redirections are not honored.
>foo
or 2>&1
is just another word created by string-splitting, rather than a shell directive.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…