收到信号后,我可以使用执行某些命令trap
.例:
trap 'echo hello world' 1 2
Run Code Online (Sandbox Code Playgroud)
如果收到任何指定的信号,则显示"hello world".
但是如何打印/识别收到的信号名称?
per*_*man 42
(如果您只有信号的编号并想要名称,则kill -l $SIGNAL_NUM
打印信号的名称;您可以通过使用信号名称而不是呼叫中的数字来避免这种情况,trap
如下所示.)
这个答案说,识别你在bash中捕获的信号的唯一方法是为你想要捕获的每个不同信号写一个单独的包装器.关于同一问题的另一个答案提供了一个包装函数来为您完成:
码:
#!/bin/bash
trap_with_arg() {
func="$1" ; shift
for sig ; do
trap "$func $sig" "$sig"
done
}
func_trap() {
echo "Trapped: $1"
}
trap_with_arg func_trap INT TERM EXIT
echo "Send signals to PID $$ and type [enter] when done."
read # Wait so the script doesn't exit.
Run Code Online (Sandbox Code Playgroud)
如果我运行它,那么我可以向进程发送信号,我得到输出
Trapped: INT
Trapped: TERM
Trapped: EXIT
Run Code Online (Sandbox Code Playgroud)
小智 9
在陷阱内(当通过信号触发时),$?变量最初设置为信号编号加128,因此您可以通过将陷阱操作的第一个语句设置为类似的方式将信号编号分配给变量
sig=$(($? - 128))
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用kill命令获取信号的名称
kill -l $sig
Run Code Online (Sandbox Code Playgroud)
for s in {1..64}; do trap "echo trap $s" $s; done
Run Code Online (Sandbox Code Playgroud)
或者没有 bash-isms
s=1; while [ $s -le 64 ]; do trap "echo trap $s" $s; s=$((s+1)); done
Run Code Online (Sandbox Code Playgroud)
设置 64 个单独的陷阱,每个陷阱对应一个可能的信号。
还有一个“信号”0 没有包含在上面,因为它并不是真正的信号。没有其他进程可以将其发送到您的进程。它实际上是一个钩子,只是使用信号/陷阱接口作为安装钩子的方式。它捕获的是事件而不是信号,事件就是exit。
trap "do stuff" 0
shell 退出时执行“do stuff”。
您可以使用上面的 {0..64} 或 s=0 来包含它。
我认为这对于这个问题没有用,因为它不是您需要检测的信号,例如“我刚刚收到什么信号?”
您总是会在 shell 退出之前得到一个。
但您可能希望它检测到它没有运行,这是检测 KILL 的一种方法。
9 包含在上面的 1-64 中,但该特定陷阱永远不会真正执行,因为 KILL 会立即终止,并且该进程根本没有机会执行任何其他操作,包括运行任何信号处理程序代码,对于 9 或 0 或其他任何内容。
因此,如果您在 0 上有一个陷阱并且它没有运行,则表明 shell 收到了 KILL。(或者 trap 0 代码已经损坏,无法告诉您它已运行,或者电源线已拔出,或者脚本仍在运行,等等。)
我几乎在每个脚本上都使用它来进行最可靠、最方便的临时文件清理。无论脚本是否正常运行,或者是否在中间任何地方中断,trap 0 代码始终仍然作为退出前的最后一件事运行。
一个简单的方法来做到这一点:
_handler() {
signal=$1
echo signal was $signal
}
trap '_handler SIGTERM' SIGTERM
trap '_handler SIGINT' SIGINT
Run Code Online (Sandbox Code Playgroud)
小智 7
参考$?
上面的解决方案:$?
会反映上次执行命令的退出码。考虑一下:
#!/bin/bash
trap 'echo CODE: $?; exit 1' 1 2 3 15
sleep 3600
Run Code Online (Sandbox Code Playgroud)
如果您运行它并按Ctrl-C,它将打印CODE: 130
。那是因为sleep
可执行文件被 SIGINT 中断并以该代码退出。
比较一下:
#!/bin/bash
trap 'echo CODE: $?; exit 1' 1 2 3 15
read X
Run Code Online (Sandbox Code Playgroud)
如果您运行它并按Ctrl-C,它将打印CODE: 0
,大概是因为该read
命令是内置命令并且退出代码规则不同(如果您中断,也会发生同样的情况while : ; do : ; done
)。
因此,$?
只有在它中断了外部命令时才告诉您信号,以及该特定程序是否未捕获该信号并使用其自己的退出代码退出。以防万一是上面的 bash 脚本:在收到 SIGINT 后,它将以代码退出1
,而不是130
.