我发现获取以下 bash 脚本不会导致命令序列在使用&&
进行管道化时停止。
sourceme.sh:
#!/usr/bin/env bash
set -o errexit
set -o | grep errexit
echo "About to error from sourceme.sh..."
$(false)
echo "sourceme.sh did not exit on error (exit code: $?)"
这是演示问题的调用。
(source sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")
我从中得到的输出如下。
errexit on
About to error from sourceme.sh...
sourceme.sh did not exit on error (exit code: 1)
sourceme.sh did not error (exit code: 0)
这与我预期的相反,我希望看到整个命令在sourceme.sh
中达到$(false)
时死亡,产生以下输出。
errexit on
About to error from sourceme.sh...
继续奇怪的是,如果我使用;
代替&&
,那么我希望命令继续,但事实并非如此。
$ (source sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")
errexit on
About to error from sourceme.sh...
因此,在获取文件时,&&
的行为符合我期望;
的行为,;
的行为与我希望&&
的行为相同。
我对这应该如何工作的理解哪里出了问题?
粗略地说,这个想法是set -o errexit
(或者,等效地,set -e
)导致 shell 在未捕获的错误时退出。&&
遵循source sourceme.sh
的事实意味着捕获了错误。
因此,如果source
和echo
命令与;
连接,shell 将退出:
$ (source sourceme.sh ; echo "sourceme.sh did not error (exit code: $?)")
errexit on
About to error from sourceme.sh...
但是,如果source
和echo
命令与&&
连接,则 shell 假定您已预见到source
命令失败的可能性,并且 shell 不会退出:
$ (source sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")
errexit on
About to error from sourceme.sh...
sourceme.sh did not exit on error (exit code: 1)
sourceme.sh did not error (exit code: 0)
set -e
的许多行为可能被认为是奇怪的或意外的。 有关更多示例,请参阅为什么设置 -e(或设置 -o errexit,或陷阱 ERR)不执行我预期的操作?。
文档
set -e
的行为(errexit)在man bash
中定义得更精确:
-e
如果管道(可能包含单个简单命令)、列表或复合命令,请立即退出(请参阅 SHELL 上面的语法),以非零状态退出。 外壳确实 如果失败的命令是命令列表的一部分,则不退出 紧随一段时间或直到关键字,测试的一部分 在 if 或 ELIF 保留字之后,执行的任何命令的一部分 在 && 或 ||列表,但最终 && 或 || 后面的命令除外, 管道中除最后一个命令之外的任何命令,或者如果命令返回 值正在与 ! 反转。 如果复合命令不是 subshell 返回非零状态,因为命令失败而 -e 被忽略,外壳不退出。 陷阱 如果设置了 ERR,则在外壳程序退出之前执行。 此选项适用 到外壳环境和每个子壳 环境单独(请参阅上面的命令执行环境), 并可能导致子壳在执行所有 子外壳中的命令。
如果化合物 命令或 shell 函数在 -e 所在的上下文中执行 忽略,在 复合命令或函数体将受到 -e 设置的影响, 即使设置了 -e 并且命令返回失败状态。 如果
复合命令或 shell 函数在 忽略 -e 的上下文,该设置将没有任何影响 直到复合命令或包含函数调用的命令 完成。
同义词
如man bash
和help set
中所述,以下两个命令是等效的:
set -e
set -o errexit
你观察到的是这个。
此命令不会在出错时退出:
(source sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")
但是这个命令:
(bash sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")
退出如此&&
并不是这种怪异的唯一原因。正是source
和&&
的结合使它无法退出。
您还注意到,使用;
而不是&&
会使其退出,如以下命令所示:
(bash sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")
此行为实际上取决于正在使用的 bash 版本。
当我在OSX上运行此命令时bash 3.2.57(1)-release
它确实退出:
(source sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")
errexit on
About to error from sourceme.sh...
但是,在 bash 版本上,4.2
及以上版本的行为会发生变化,set -e
在与&&
一起使用时不会退出。
所以底线是set -e
是非常不可靠的,应该避免依赖于它在重要的 shell 脚本中的行为。