iga*_*gal 20

简答

run by 的命令eval在当前 shell 中执行,通过管道传输的命令bash在 sub-shell 中执行,例如:

> echo 'x=42' | bash; echo $x

> eval 'x=42'; echo $x
42
Run Code Online (Sandbox Code Playgroud)

更长的答案

在评论中声称在bash(>=4.2) 的更新版本中,第一个命令也可能具有相同的效果。然而,情况似乎并非如此。

实际上有几个因素会导致管道命令不在当前会话中运行:管道和bash命令。

大多数情况下,管道命令在子 shell 中运行。Bash 手册(第 3.2.2 节:管道)有以下内容:

管道中的每个命令都在其自己的子 shell 中执行(请参阅命令执行环境)。

正如评论中所指出的,可以通过lastpipe选项修改此行为。Bash 手册(第 4.3.2 节:The Shopt Builtin)对该lastpipe选项有以下说明:

最后管

如果设置,并且作业控制未处于活动状态,shell 将运行当前 shell 环境中未在后台执行的管道的最后一个命令。

我们可以通过以下方式验证情况是否如此。

首先启用lastpipe

> shopt -s lastpipe
Run Code Online (Sandbox Code Playgroud)

然后禁用作业控制:

> set +m
Run Code Online (Sandbox Code Playgroud)

现在执行从管道内设置变量的命令:

> unset x
> echo x=42 | while IFS= read -r line; do eval "${line}"; done;
> echo $x
42
Run Code Online (Sandbox Code Playgroud)

请注意,我们使用while循环和read命令作为解决方法,因为该eval命令无法从 stdin 读取其输入(因此无法从管道获取其输入)。

这个例子演示了管道中最右边的命令实际上可以在当前 shell 中执行。然而,这实际上并不影响我们原来的例子。即使lastpipe启用和禁用作业控制,我们仍然会在管道到 时得到以下结果bash

> echo 'x=42' | bash; echo $x

>
Run Code Online (Sandbox Code Playgroud)

这是因为bash命令本身在子 shell 中执行其输入。

  • @dave_thompson_085 `bash` 命令可能会根据选项在顶层 shell 中执行,但 `x=42` 肯定会在可以合理称为子 shell 的东西中运行。 (5认同)