【发布时间】:2012-02-13 06:59:22
【问题描述】:
收到信号后,我可以使用trap 执行一些命令。示例:
trap 'echo hello world' 1 2
如果接收到任何指定的信号,则会显示“hello world”。
但是如何打印/识别接收到的信号名称?
【问题讨论】:
收到信号后,我可以使用trap 执行一些命令。示例:
trap 'echo hello world' 1 2
如果接收到任何指定的信号,则会显示“hello world”。
但是如何打印/识别接收到的信号名称?
【问题讨论】:
for s in {1..64} ;do trap "echo trap $s" $s ;done
或者没有 bash-isms
s=1 ;while [ $s -le 64 ] ;do trap "echo trap $s" $s ;s=$((s+1)) ;done
设置 64 个单独的陷阱,每个可能的信号一个。
【讨论】:
参考上面$?的解决方案:$?会反映最后执行命令的退出码。考虑一下:
#!/bin/bash
trap 'echo CODE: $?; exit 1' 1 2 3 15
sleep 3600
如果你运行它并按下 Ctrl-C,它会打印出CODE: 130。这是因为 sleep 可执行文件被 SIGINT 中断并以该代码退出。
比较一下:
#!/bin/bash
trap 'echo CODE: $?; exit 1' 1 2 3 15
read X
如果你运行它并按下 Ctrl-C,它会打印出CODE: 0,大概是因为read 命令是内置的并且退出代码规则不同(如果你要中断,也会发生同样的情况while : ; do : ; done)。
所以,$? 仅在中断外部命令时告诉您信号,并且如果该特定程序没有捕获信号并使用自己的退出代码退出。以防万一是上面的 bash 脚本:收到 SIGINT 后,它将以代码 1 退出,而不是 130。
【讨论】:
read X 更改为(read X) 可以作为一种解决方法。
(如果您只有信号的编号并且想要名称,kill -l $SIGNAL_NUM 会打印信号的名称;您可以通过在对 trap 的调用中使用信号名称而不是数字来避免这种情况,如下所示。 )
This answer 表示无法访问信号名称,但如果您对捕获的每个信号都有单独的函数,那么您已经知道信号名称:
trap 'echo trapped the HUP signal' HUP
trap 'echo different trap for the INT signal' INT
在许多情况下,这可能就足够了,但another answer on that same question 使用该事实提供了一种变通方法来伪造您想要的行为。它接受一个函数和一个信号列表,并为使用信号名称调用的函数上的每个信号设置一个单独的陷阱,因此在内部它实际上是每个信号的单独函数,但它看起来像单个函数上的单个陷阱,它获取信号名称作为参数:
代码:
#!/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.
如果我运行它,那么我可以向进程发送信号并得到类似的输出
Trapped: INT
Trapped: TERM
Trapped: EXIT
【讨论】:
trap_with_arg() 不会阻止。它循环输入,设置陷阱处理程序,然后返回。
sleep 中间时发出信号,则在sleep 完成之前它不会执行您的陷阱。它不是异步的。陷阱将捕获脚本中命令之间的信号。
sleep 1000 & pid=$! ; echo "waiting for ${pid}" ; wait $pid之类的东西而不是read,这样更清楚发生了什么
一个简单的方法:
_handler() {
signal=$1
echo signal was $signal
}
trap '_handler SIGTERM' SIGTERM
trap '_handler SIGINT' SIGINT
【讨论】:
在陷阱内(通过信号触发时),$?变量最初设置为信号编号加 128,因此您可以通过将陷阱操作的第一条语句设置为类似的内容来将信号编号分配给变量
sig=$(($? - 128))
然后您可以使用 kill 命令获取信号的名称
kill -l $sig
更新:如 cmets 中所述,这不适用于某些内置 shell 命令。让这些设置陷阱的 $?变量,它们可以在子shell中运行,例如
(read)
而不是
read
【讨论】:
$? 在信号处理程序的开头为 0,即使它已被 @ 调用987654327@信号。