语法错误时不执行“ERR”陷阱

Posted

技术标签:

【中文标题】语法错误时不执行“ERR”陷阱【英文标题】:`ERR` trap is not executed when syntax error 【发布时间】: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 bashset -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 暗示这两种机制的作用方式相同,但显然它们没有而且它们从来没有。 【参考方案1】:

这是因为多年来set -ebash 中的实现方式很奇怪。它涉及许多特定情况,预计在这些情况下会起作用。

来自man bash

trap ... ERR 如果 sigspec 是 ERR,则只要管道 (可能由单个简单命令组成)、a列表或复合命令返回非零退出状态,受以下条件限制: 如果失败的命令是紧跟在whileuntil 关键字之后的命令列表的一部分,则不会执行ERR 陷阱... ...if 语句中的部分测试... ...在&amp;&amp;|| 列表中执行的命令的一部分,除了最后一个&amp;&amp;|| 之后的命令... ...管道中的任何命令,但最后一个... ...或者如果使用! 反转命令的返回值。 这些与 errexit -e 选项所遵循的条件相同。

您在此示例代码中拥有的是shell expansion error,上面提到的子句从不真正适用。在上述所有子句中,一个不正确的命令运行/完成,并且设置一个返回码返回给你的 ERR 陷阱以触发。

但是当遇到echo $(( $#array[@] - 1)) 行时,在算术展开失败后不久,没有实际运行的命令会触发ERR 陷阱,因为要触发陷阱,该命令需要完成。来自man bash页面

如果Bash 正在等待命令完成并接收到已设置陷阱的信号,则在命令完成之前不会执行陷阱。

【讨论】:

实际上 ERR 陷阱最初是在 korn shell 中实现的,而 bash 只是模拟了这种行为。 请参阅我在 OP 中的补充。【参考方案2】:

ERR 陷阱从未捕获语法错误,并且不是为此而设计的。来自man bash

  If  a  sigspec  is  ERR,  the command arg is executed whenever a
  pipeline (which may consist of a single simple command), a list,
  or a compound command returns a non-zero exit status ...

在这种情况下,该命令永远不会执行,因此不会返回非零状态,脚本在此之前失败。

【讨论】:

man bashIf a sigspec is ERR, the command ard is executed whenever ... These are the same conditions obeyed by the errexit (-e) option. 在我的例子中,set -e 捕获了语法错误,所以如果trap &lt;command&gt; ERR 在与set -e 相同的条件下工作,则应该执行ERR 陷阱,我相信。 @ynn 但它在与set -e 相同的条件下不起作用,这就是重点! ERR 陷阱在命令执行后 被调用,在这种情况下命令永远不会执行。 但是man bashset -e 命令部分也说Exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound command (see SHELL GRAMMAR above), exits with a non-zero status. 所以如果你的解释是正确的,shell 不应该退出(应该执行echo "after")。但是,就我而言,shell 出于某种原因退出。 @ynn:您缺少手册页中trap 的摘录。在命令完成之前不会触发陷阱,但语法错误的行永远不会完成 @ynn 标准说 -e 当此选项打开时,当任何命令失败时(由于第 2.8.1 节中列出的任何原因,Shell 错误的后果或返回退出status 大于零),shell 将立即退出,所以这只是一个文档问题

以上是关于语法错误时不执行“ERR”陷阱的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL:语法错误

java开发实战之语法篇

如何导入可能有语法错误的模块?

插入 JSON 对象时出现 Python pymysql 语法错误

访问错误陷阱

为啥在包含 Microsoft SDK 中的 BluetoothAPIs.h 时会收到语法错误?