【发布时间】:2020-02-07 13:41:12
【问题描述】:
根据man bash,
set -e如果 (snip) 立即退出。
ERR上的陷阱(如果已设置)会在 shell 退出之前执行。
但是,下面的脚本不会调用ERR 陷阱。
trap 'echo ERR; sleep 1' ERR
trap 'echo EXIT; sleep 1' EXIT
set -e
array=(a b c)
echo $(( ${#array[@] - 1)) #note the closing bracket } is forgotten
echo "after"
预期的结果是
$ bash script.sh
4.sh: line 7: ${#array[@] - 1: bad substitution
ERR
EXIT
# and then shell exits
实际结果是
$ bash script.sh
4.sh: line 7: ${#array[@] - 1: bad substitution
EXIT
# and then shell exits
如果我删除set -e这一行,那么
$ bash script2.sh
4.sh: line 7: ${#array[@] - 1: bad substitution
after #the last echo command is executed
EXIT
这意味着set -e 捕获了语法错误。为什么在 shell 确实 退出时没有调用 ERR 陷阱?
环境:
我在两台机器上测试了脚本。
$ bash --version
GNU bash, version 4.4.12(1)-release (arm-unknown-linux-gnueabihf)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ bash --version
GNU bash, version 5.0.11(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
补充:
根据发布的答案,陷阱没有执行是很自然的,这是因为echo $(( ${#array[@] - 1)) 没有完成执行以返回退出状态。我的理解对吗?
但是,man bash 将 set -e 解释为
如果管道(可能由单个简单命令组成)、列表或复合命令(参见上面的 SHELL GRAMMAR)以非零状态退出,则立即退出。
我认为这也需要命令来完成。如果echo $(( ${#array[@] - 1))没有完成它的执行,我相信set -e在这里不应该工作,echo "after"应该被执行。
补充2:
根据oguz ismail's comment,这是一个文档问题。 POSIX 说set -e 应该在什么时候退出shell
-
返回非零退出状态(如您在
man bash中所见) -
或 shell错误发生
和shell error includes syntax error。但是man bash 缺少对第二种情况的解释,对吧?如果是这样,除了man bash 没有准确解释实现以及声明“这些是errexit (-e) 选项遵循的相同条件”这一事实之外,一切都是一致的。在trapman bash的解释中找到。
【问题讨论】:
-
我相信这个问题是这里的主题,但是您可能还想在Unix&Linux SE 上发帖,您可能有更多机会快速得到答案。
-
@Aaron 谢谢你的建议。如果没有好的答案,我会考虑。
-
您似乎错过了
ERR测试返回代码来自 已完成 命令的点,而不是来自尚未执行的命令(怎么可能?)。您所观察到的是正确和预期的行为。 -
@cdarke 好的,我明白了。但是为什么
set -e会捕获语法错误呢?man bash并没有说它捕获语法错误。 (POSIX 这么说(参见 OP 中的 Suppliment2),但bash不符合 POSIX。) -
查看源代码,
set -e(errexit) 和traps ERR使用不同的代码和机制来测试和处理错误。set -e立即退出,ERR陷阱仅在命令运行时进行测试,这就是set -e捕获语法错误而ERR陷阱没有的原因。man bash暗示这两种机制的作用方式相同,但显然它们没有而且它们从来没有。