我用以下代码捕获 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
至于仅调用一次陷阱,一种选择是ERR
在INT
陷阱内重置陷阱:
...
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