同时捕获 INT 和 ERR,但回调被执行了多次

同时捕获 INT 和 ERR,但回调被执行了多次

我用以下代码捕获 INT 和 ERR

set -ex -o pipefail

dest=$(mktemp -d)
cd "$dest"

trap "echo; echo Clean up; rm -rf $dest" INT ERR
sleep 9999

当我按下^C清理回调时,会执行多次

++ echo Clean up
Clean up
++ rm -rf /tmp/tmp.KYXL110516
++ echo

++ echo Clean up
Clean up
++ rm -rf /tmp/tmp.KYXL110516

这是预期的行为吗?是否可以只执行一次?

答案1

您同时收到 INT 和 ERR 信号; SIGINT 被传递给sleep,后者以非零返回码退出。然后,非零返回代码会触发 SIGERR 陷阱。

如果 sigspec 是 ERR,则只要管道(可能由单个简单命令组成)、列表或复合命令返回非零退出状态,就会执行命令 arg...

一个例子,查看单独的陷阱:

set -ex -o pipefail
trap "echo Clean up for INT" INT
trap "echo Clean up for ERR" ERR
sleep 9999

执行,然后Control-C:

+ trap 'echo Clean up for INT' INT
+ trap 'echo Clean up for ERR' ERR
+ sleep 9999
^C++ echo Clean up for INT
Clean up for INT
++ echo Clean up for ERR
Clean up for ERR

至于仅调用一次陷阱,一种选择是ERRINT陷阱内重置陷阱:

...
trap "echo Clean up for INT; trap ERR" INT ERR
...

...这导致:

+ trap 'echo Clean up for INT; trap ERR' INT ERR
+ sleep 9999
^C++ echo Clean up for INT
Clean up for INT
++ trap ERR

答案2

在中bash,您可以简单地将其替换为trap "my-cleanup-command" EXIT.当脚本退出时,包括发送 SIGINT 时,都会运行此陷阱。在我看来,这通常是更优雅的方法......

除了它是特定于 bash 的事实之外。 POSIX 中提到了 EXIT 陷阱,但未指定其行为。在许多其他 shell 中,如果 shell 由于信号而退出,则 EXIT 陷阱不会运行。

https://unix.stackexchange.com/a/57960/29483

有时我真的很讨厌 shell 脚本:)。


看看链接的答案,我认为确保第一个陷阱后没有代码运行的一种不太特定于 bash 的方法是立即退出。您可能想要使用不同的方法来执行此操作,但这是我会尝试的最简单的方法。

cleanup() {
    echo "Clean up"
    rm -rf "$dest"
}
trap cleanup EXIT
trap "exit 1" INT ERR

相关内容