23

我在这里有点困惑。我的目标是当脚本中的任何命令失败时,让 bash 脚本以非零退出代码退出。使用 -e 标志,我认为情况会如此,即使在使用子外壳时也是如此。下面是一个简化的例子:

#!/bin/bash -e

(false)

echo $?
echo "Line reached!"

这是运行时的输出:

[$]>Tests/Exec/continuous-integration.sh 
1
Line reached!

Bash 版本:CentOS 上的 3.2.25

4

2 回答 2

14

似乎这与您的bash. 在我可以访问的机器上,bash 版本 3.1.17 和 3.2.39 会表现出这种行为,而 bash 4.1.5 则不会。

虽然有点难看,但适用于两个版本的解决方案可能是这样的:

#!/bin/bash -e

(false) || exit $?

echo $?
echo "Line reached!"

bash 源代码更改日志中有一些与set -e选项错误相关的注释。

于 2013-02-20T01:31:51.050 回答
2

我在 El Capitan 之前的 SuSE 11.3 和 Mac OS 上的 bash 版本 3.2.51 中看到了这种行为。El Capitan 上的 Bash 3.2.57 具有“正确”的行为,即像 bash 4。

但是,上面提出的解决方法是添加“|| exit $?” 在 subshel​​l 的结束括号之后,无论什么版本的 bash,都会破坏 -e 标志的意图。来自 man bash:

-e 如果简单命令(参见上面的 SHELL GRAMMAR)以非零状态退出,则立即退出。如果失败的命令是紧跟在 while 或 until 关键字之后的命令列表的一部分、if 语句中测试的一部分、&& 或 || 的一部分,则 shell 不会退出 列表, ...

一个子shell,后跟“|| exit $?” 显然算作命令列表;并且 bash -e 标志将不适用于子外壳内的任何命令。尝试一下:

$ set -e
$ ( echo before the error; false; echo after the error, status $?; ) || echo after the subshell, status $?
before the error
after the error, status 1
$

因为子shell 后跟||,所以即使使用set -e,也会运行“错误后回显”。不仅如此,子shell退出0,因为那个“回声”跑了。所以“||退出$?” 甚至不会运行“退出”。可能不是我们想要的!

据我所知,以下公式与 bash 版本兼容,无论它们是否在 subshel​​l 之后尊重 bash -e 。如果碰巧重置了 -e 标志,它甚至会正常运行:

在 bash 脚本中每个子 shell 的右括号之后立即添加以下行:

case $?/$- in ( 0/* ) ;; ( */*e* ) exit $? ;; esac # honor bash -e flag when subshell returns
于 2015-12-18T22:57:40.793 回答