6

What is the difference between using:

eval 'echo "foo"'

and

echo 'echo "foo"' | bash

is there any?

1 Answers1

20

Short Answer

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

Longer Answer

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.

igal
  • 9,886
  • 1
    that is a good observation, didn't realize that, but yeah I guess that's how pipes work – Alexander Mills May 07 '18 at 04:44
  • 1
    Just curious, what is the reason why you did not accept the answer? – Ondřej Xicht Světlík May 07 '18 at 06:36
  • 1
    For bash 4.2+ when running a script (or more exactly when jobcontrol is off) and shopt 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
  • 5
    @dave_thompson_085 The 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
  • @dave_thompson_085 As @hvd says, I don't think that in this case the 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
  • 1
    @AlexanderMills Actually, the pipe isn't the only issue. The 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
  • 1
    nice that is good info, who's got love for the OP – Alexander Mills May 09 '18 at 04:03
  • 1
    igal: you're right, I spoke too fast. Sorry. – dave_thompson_085 May 10 '18 at 09:00