我正在尝试在 Bash 脚本中编写信号处理程序,并在 Ubuntu 16.04 机器下进行测试。这里我使用以下命令trap
:
#!/bin/bash
trap "echo hi!" SIGINT SIGTERM
echo "pid is $$"
while true; do
sleep 1;
done
运行后./test.sh
,按Ctrl+会在当前终端下C立即显示消息。但是,在另一个终端下打印会延迟 1 秒。hi!
kill <pid>
hi!
有人能说出为什么会有这种差异吗?有没有一种方法可以无论发生什么情况都立即触发陷阱命令?(例如睡眠循环)。
答案1
谁能说出为什么会有这种差异?
Ctrl+也C能达到,而 while不能。实际上,后一种方式需要在打印之前自行退出。所谓的“1 秒延迟”最多约为 1 秒,平均约为 0.5 秒(如果操作系统忙于其他任务,则可能需要更长时间)。sleep
kill <pid>
sleep
hi!
你可以通过使用
pv -qL 1 <<< "0123456789"
代替sleep 1
。
注意
kill -- -<pid> # note the minus sign before PID
会将信号发送给整个进程组,因此sleep
(或pv
)也是如此。
有没有一种方法,无论发生什么情况,总是立即触发陷阱命令?
来自其中一个答案:
如果 bash 正在等待命令完成并收到已设置陷阱的信号,直到命令完成时,陷阱才会执行. 当 bash 通过内置命令等待异步命令时
wait
,收到已设置陷阱的信号将导致wait
内置命令立即返回,退出状态大于 128,随后立即执行陷阱。因此,异步运行你的外部程序并使用
wait
。使用将其终止$!
。
在你的情况下,这种方法会导致以下快速和肮脏的脚本:
#!/bin/bash
trap 'kill "$!"; echo hi!' SIGINT SIGTERM
echo "pid is $$"
while true; do
sleep 1 &
wait
done
答案2
无需sleep 1
尝试sleep 1 <&0 & wait
,然后您就会发现它立即退出。
但该sleep
命令仍将继续运行。
这是可行的,因为 bash 的wait
命令可以被信号中断,但内部等待不能。
当然,<&0
睡眠不需要这样做,但是对于以这种方式在后台运行的其他命令,除非我们故意以这种方式连接它,否则 stdin 将被断开连接。