为什么eval退出子壳中和&&设置-e?

时间:2016-06-29 22:23:53

标签: bash shell eval exit subshell

为什么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

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

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

eval "false && true" || :

这样eval命令就不会因为错误而退出我的子shell,因为这可以解决这个问题:

$ 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="$1"
    local remote="$2"
    if [ ! -z "$remote" ]; then
        $SSH $remote "$command" || :
    else
        eval "$command" || :
    fi
}

我在我的脚本中使用set -e。此函数中的ssh也会出现同样的问题 - 如果ssh脚本中的最后一个命令是一个提前终止的复合命令,则整个命令将以错误终止。我希望这样的命令行为好像它们在本地执行一样 - 早期终止复合命令不应该导致ssh或eval返回1,导致整个命令失败。如果我在我的eval语句或ssh语句的末尾添加|| :,那么所有这些命令都会成功,即使它们不应该是因为eval中的最后一个命令或ssh&#39 ; d命令失败。

非常感谢任何想法。

2 个答案:

答案 0 :(得分:2)

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

那个偏僻。 。

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

如果您要运行eval "false && true; true",则不会看到此行为,因为eval计算结果为true(零)。 (请注意,虽然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"

答案 1 :(得分:0)

eval将自己的命令视为自己的退出代码。

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