考虑以下:
#!/bin/bash
trap 'echo $?' INT
kill -INT $$
输出:0
在这里我期望130
我的系统。当然,如果我做了 aCtrl + C
那么我会得到130
。
HUP
对于任何其他信号(如或 )也会发生同样的情况TERM
。我发现这种行为令人惊讶,因为如果您设置了trap
捕获许多信号的设置,那么就无法使用调用处理程序的信号的正确错误代码退出:
#!/bin/dash
exit_abrupt() {
exit_code=$?
echo "Encountered an error, cleaning up..." >&2
# *clean up*
exit "$exit_code" # This will return 0!
}
trap exit_abrupt HUP INT TERM
kill -HUP $$
我测试过 Bash、Dash 和 ZSH,它们都表现出这种行为。这就是跨 shell 的工作方式吗?它是否由 POSIX 记录(有人可以指出该文档)吗?我还能如何知道跨 shell 运行的正确退出代码?
最好的POSIX 文档我发现读到:
“$”的值?陷阱操作完成后应为调用陷阱之前的值。
对我来说,这听起来像是在所有情况下都应该通过信号,但事实并非如此,所以我一定错过了一些东西......
答案1
$?
包含最后运行和等待的命令的退出状态。您会在以下位置找到它:
$ bash -c 'trap "echo \$?" INT; sleep 10; exit'
^C130
报告 130 是因为两者都sleep
在bash
^C 上收到了 SIGINT,bash 在返回后运行了处理程序并打印了该命令sleep
的退出状态。sleep
在:
$ bash -c 'trap "echo \$?" INT; kill -s INT "$$"; exit'
0
您可以获得命令的退出状态kill
,该命令是调用 SIGINT 处理程序之前 bash 运行的最后一个命令。
$ bash -c 'trap "echo \$?" INT; (trap "" INT; sleep 3; exit 123); exit'
^C123
sleep
忽略 SIGINT,您会看到按Ctrl+后C,您仍然必须等待运行的子 shellsleep
返回,此时处理程序会打印该子 shell 的退出状态 (123)。
如果要为多个信号安装相同的处理程序,可以将信号传递给处理程序:
handler() {
local signal="$1"
echo "I got $signal signal"
}
for signal in INT HUP TERM QUIT; do
trap "handler $signal" "$signal"
done
顺便说一句,你会发现:
bash -c '(trap "" INT; sleep 3; exit 123); exit'
即使您尝试使用Ctrl+中断它c,也会以 123 退出状态退出,这实际上是 bash 中的设计。更多相关信息,请访问在终端中输入 ctrl-c 时,为什么前台作业在完成之前不会终止?
答案2
这是我的最终解决方案,用于将EXIT
陷阱与致命信号陷阱(无论是通过击键接收还是发送到 shell 进程的信号接收)结合起来,同时始终传递正确的退出状态,以防有人需要:
#!/bin/sh
set -e
# https://unix.stackexchange.com/questions/752570/why-does-trap-passthough-zero-instead-of-the-signal-the-process-was-killed-wit
handle_exit() {
exit_code=$?
signal="$1"
if [ "$exit_code" != 0 ] || [ "$signal" ]; then
echo "\nProgram was exited abruptly!" >&2
fi
# Or do clean up here...
if [ "$exit_code" != 0 ]; then
trap -- - EXIT
exit "$exit_code"
elif [ "$signal" ]; then
trap -- - "$signal"
kill -s "$signal" -- $$
fi
}
# POSIX sh doesn't include signals in its EXIT trap so list them ourselves
# SIG prefixes removed for POSIX sh compatibility
for signal in HUP INT TERM; do
# shellcheck disable=SC2064
trap "handle_exit $signal" "$signal"
done
trap handle_exit EXIT
# Your code starts here...
这trap -- - EXIT
是必要的,因为如果您触发文字INT
( Ctrl + C
),那么INT
陷阱将在陷阱之前正常触发EXIT
。在这种特殊情况下(由于文字Ctrl + C
),$exit_code
也将被设置为130
。因此,我们从分支EXIT
中删除陷阱if
以避免触发处理程序两次。同样,trap -- - "$signal"
避免永远递归调用处理程序,直到发生堆栈溢出。
在所有外壳(甚至 Posh)中,它都像一个魅力。