为什么 eval 使用 set -e 退出 subshel​​l mid-&&?

Why does eval exit subshell mid-&& with set -e?

为什么 bash 使用子 shell:

中的复合命令执行我期望的操作
$ bash -x -c 'set -e; (false && true; echo hi); echo here'
+ set -e
+ false
+ echo hi
hi
+ echo here
here

但不要做我在这里期望的事情:

$ bash -x -c 'set -e; (eval "false && true"; echo hi); echo here'
+ set -e
+ eval 'false && true'
++ false

基本上,区别在于'eval'-执行复合命令和只执行复合命令。当 shell 执行复合命令时,复合命令中的非终端命令失败不会导致整个复合命令失败,它们只是终止命令。但是当 eval 运行复合命令并且任何非终端子命令以错误终止命令时,eval 以错误终止命令。

我想我需要像这样格式化我的 eval 语句:

eval "false && true" || :

这样 eval 命令就不会错误地退出我的 subshell,因为这正如我所期望的那样工作:

$ bash -x -c 'set -e; (eval "false && true" || :; echo hi); echo here'
+ set -e
+ false
+ echo hi
hi
+ echo here
here

我遇到的问题是我写了一个函数:

function execute() {
    local command=""
    local remote=""
    if [ ! -z "$remote" ]; then
        $SSH $remote "$command" || :
    else
        eval "$command" || :
    fi
}

我在脚本中使用 set -e。这个函数中的 ssh 也会出现同样的问题——如果 ssh 脚本中的最后一个命令是一个提前终止的复合命令,则整个命令会因错误而终止。我希望这样的命令表现得好像它们在本地执行一样 - 提前终止复合命令不应导致 ssh 或 eval 到 return 1,从而使整个命令失败。如果我在我的 eval 语句或我的 ssh 语句的末尾附加 || :,那么所有这些命令都会成功,即使它们不应该因为 eval'd 或 ssh'd 命令中的最后一个命令失败。

如有任何想法,我们将不胜感激。

eval 算作自己的命令,有自己的退出代码。

由于 eval "false && true" returns 退出代码为 1,因此触发 set -e

我还应该提到 set -e 非常容易出错;请参阅 http://mywiki.wooledge.org/BashFAQ/105 以获取大量示例。因此,最佳 解决方案可能是放弃它,并编写自己的逻辑来检测错误并中止。


那不碍事。 . .

这里的问题是 eval "false && true" 是单个命令,计算结果为 false(非零),因此 set -e 在该命令 运行s 后中止。

如果您改为 运行 eval "false && true; true",您将不会看到此行为,因为 eval 的计算结果为真(零)。 (请注意,尽管 eval 确实实现了 set -e 行为,但它遵守 false && true 是非中止的规则。)

顺便说一句,这实际上并不是 eval 特有的。出于同样的原因,子 shell 会给出相同的结果:

$ bash -x -c 'set -e; (false && true); echo here'
+ set -e
+ false

解决您的问题的最简单方法可能就是 运行 如果到达终点,额外 true

$SSH $remote "set -e; $command; true"
eval "$command; true"