我在命令行中使用set -e内部(...)但在执行命令时它不会退出子shell false.
代码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
Run Code Online (Sandbox Code Playgroud)
但是,如果我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
Run Code Online (Sandbox Code Playgroud)
有趣的是,如果我删除&&并||在子壳后分开,那么(...)也表现得很好.
代码3:
( set -e; echo "$BASHPID: start"; false; echo "foobar"; date; )
5940: start
Run Code Online (Sandbox Code Playgroud)
所以我得到的结论是:
(...) 表现不同于 bash -c (...) && false表现不同于(...)和改变子shell的行为.我在解释这种奇怪的(至少对我而言)行为时犯了一些明显的错误吗?
set -ewith的行为&&是有意的。
当命令是&&序列的一部分时,它明确不会触发。
来自 POSIX 规范:
-e
当此选项打开时,当任何命令失败时(由于 Shell 错误的后果中列出的任何原因或返回大于零的退出状态),shell 应立即退出,但以下例外:多命令管道不应导致 shell 退出。仅考虑管道本身的故障。
当执行 while、until、if 或 elif 保留字(以 ! 开头的管道)后面的复合列表时,应忽略 -e 设置。保留字,或 AND-OR 列表中除最后一个以外的任何命令。
如果子 shell 命令以外的复合命令的退出状态是在 -e 被忽略时失败的结果,则 -e 不适用于该命令。
此要求分别适用于 shell 环境和每个子 shell 环境。例如,在:
Run Code Online (Sandbox Code Playgroud)set -e; (false; echo one) | cat; echo twofalse 命令导致子 shell 退出而不执行 echo one;然而,执行 echo 2 是因为管道的退出状态 (false; echo one) | 猫为零。
我认为这里的区别在于 shell 是否知道这个事实。
(...)可能是因为当前 shell 执行它,也bash -c可能不是,因为那是一个完全外部的进程(或其他进程)。
最后一点是猜测,在我看来,这里的子 shell 行为完全没有帮助并且不可靠set -e(这就是为什么许多人建议避免它)。
尽管如果显式运行的 shell 不存在脚本组合问题,那么这似乎也可能为该可靠性问题提供一种“逃生舱口”。