What is the difference between using:
eval 'echo "foo"'
and
echo 'echo "foo"' | bash
is there any?
What is the difference between using:
eval 'echo "foo"'
and
echo 'echo "foo"' | bash
is there any?
The command run by eval
is executed in the current shell and the command piped to bash
is executed in a sub-shell, e.g.:
> echo 'x=42' | bash; echo $x
> eval 'x=42'; echo $x
42
In the comments the claim was made that in more recent versions of bash
(>=4.2) the first command could also have the same effect. However this does not appear to be the case.
There are actually a couple of factors which cause the piped command not to run in the current session: the pipe and the bash
command.
For the most part, piped commands run in subshells. The Bash Manual (Section 3.2.2: Pipelines) has the following to say:
Each command in a pipeline is executed in its own subshell (see Command Execution Environment).
As pointed out in the comments, this behavior can be modified via the lastpipe
option. The Bash Manual (Section 4.3.2: The Shopt Builtin) has the following to say about the lastpipe
option:
lastpipe
If set, and job control is not active, the shell runs the last command of a pipeline not executed in the background in the current shell environment.
We can verify that this is the case as follows.
First enable lastpipe
:
> shopt -s lastpipe
Then disable job-control:
> set +m
Now execute a command which sets a variable from within a pipe:
> unset x
> echo x=42 | while IFS= read -r line; do eval "${line}"; done;
> echo $x
42
Notice that we use the while
loop and read
command as a work-around since the eval
command cannot read its input from stdin (hence cannot get its input from a pipe).
This example demonstrates that the right-most command in the pipe can, in fact, be executed in the current shell. However this does not actually affect our original example. Even with lastpipe
enabled and job-control disabled, we still get the following result when piping to bash
:
> echo 'x=42' | bash; echo $x
>
This is because the bash
command itself executes its input in a subshell.
lastpipe
is set and the pipeline is not backgrounded, it does run the right-hand end of the pipe in the 'top' shell not a subshell. See https://unix.stackexchange.com/questions/9954/why-is-my-variable-local-in-one-while-read-loop-but-not-in-another-seemingly and https://unix.stackexchange.com/questions/136206/readarray-or-pipe-issue
– dave_thompson_085
May 07 '18 at 07:35
bash
command may be executed in the top shell depending on options, but x=42
will assuredly be run in something that can reasonably be called a subshell.
– hvd
May 07 '18 at 08:40
lastpipe
option has the effect that you're suggesting it will. Even if the pipe isn't run in a subshell the bash
command itself launches a subshell in order to execute the command that is passed to it. In other words, the examples given in my solution have the same behavior regardless of whether or not the lastpipe
option is enabled. I verified this myself on Bash v4.4.
– igal
May 07 '18 at 18:15
bash
command will execute its input in a subshell regardless of whether or not a pipe is involved. See the update to my solution for more details.
– igal
May 09 '18 at 02:25
( eval 'echo "foo"' )
– David Foerster May 07 '18 at 15:22