我在命令行的(...)
中使用set -e
,但在执行false
命令时,它不会退出子shell。
代码1:
( set -e; echo "$BASHPID: start"; false; echo "foobar"; date; ) &&
echo "$BASHPID: ok" || echo "$BASHPID: nope"
9136: start
foobar
Wed, Apr 29, 2015 7:14:24 PM
7292: ok
但是,如果我使用bash -c
创建子shell,那么它的行为与我预期的一样。
代码2:
bash -c 'set -e; echo "$BASHPID: start"; false; echo "foobar"; date;' &&
echo "$BASHPID: ok" || echo "$BASHPID: nope"
7880: start
7292: nope
有趣的是,如果我在子壳之后去除&&
和||
部分,那么(...)
也表现良好。
代码3:
( set -e; echo "$BASHPID: start"; false; echo "foobar"; date; )
5940: start
所以我得到的结论是:
(...)
的行为与bash -c
不同- CCD_ 10的行为与CCD_ 11不同
我在解释这种奇怪的(至少对我来说)行为时是否犯了一些明显的错误?
set -e
与&&
的行为是有意的。
当命令是&&
序列的一部分时,它明确不触发。
来自POSIX规范:
-e
如果此选项处于启用状态,则当任何命令失败时(由于"Shell错误的后果"中列出的任何原因或返回大于零的退出状态),Shell应立即退出,但以下情况除外:多命令管道中任何单个命令的失败都不应导致外壳退出。只应考虑管道本身的故障。
执行while后面的复合列表时,应忽略-e设置,直到if或elif保留字出现以!开头的管道为止!保留字或除最后一个之外的"与或"列表的任何命令。
如果子shell命令以外的复合命令的退出状态是由于-e被忽略时出现故障而导致的,则-e不应适用于此命令。
此要求分别适用于shell环境和每个子shell环境。例如,在:中
set -e; (false; echo one) | cat; echo two
false命令导致子shell退出而不执行echo-one;但是,由于管道的退出状态(false;echo-one)|cat为零,所以执行echo-two。
我假设这里的区别在于shell是否知道这个事实。
(...)
可能会执行,因为当前shell执行它,而bash -c
可能不会,因为这是一个完全外部的进程(或其他什么)。
最后一点是猜测,在我看来,这里的子外壳行为完全没有帮助,并使set -e
不可靠(这就是为什么许多人建议避免它)。
尽管如果显式运行的shell在脚本的组成方面没有这个问题,那么这似乎也可能为可靠性问题提供一种"逃生通道"。