我有一个两个过程的简单示例
第一个是一个简单的循环,它进行一些处理
#!/bin/bash
function signalHandler() {
echo "sig: $1 received ==> exit"
for i in {1..5}; do
echo "cleanup $i %"
sleep 1
done
trap SIGINT
kill -INT $$
}
trap signalHandler SIGUSR2
for i in {1..100000}; do
echo "doing stuff $i %"
sleep 0.1
done
我像这样启动了脚本:
./script.sh | grep "wow"
我附加了一个辅助 grep 命令只是为了演示。
当我仅向进程发送 USR2 信号grep
( kill USR2 $(pgrep grep)
) 时,整个管道被拆除 (?)。为什么script.sh
不继续?
其次,当我向整个进程组发送 USR2 信号(类似于kill USR2 -$!
)时,显然会发生同样的情况,因为grep
退出速度很快,它会破坏整个管道而不让script.sh
执行signalHandler
.我想我有一些误解,这应该如何运作?
多谢
答案1
正如 @muru 评论的那样,问题是你的脚本不处理SIGPIPE
.
以下更改将允许脚本按预期终止:
function signalHandler() {
echo "sig: $1 received ==> exit" >&2 # <--
for i in {1..5}; do
echo "cleanup $i %" >&2 # <--
sleep 1
done
trap SIGINT
kill -INT $$
}
trap signalHandler SIGUSR2
trap signalHandler SIGPIPE # <--
为了使示例简单,使用相同的信号处理程序来处理SIGPIPE
.echo
信号处理程序的输出被重定向到,STDERR
因为STDOUT
显然在 被捕获时已经被破坏了SIGPIPE
。
输出:
$ ./script.sh | grep wow
./script.sh: line 16: echo: write error: Broken pipe
sig: received ==> exit
cleanup 1 %
cleanup 2 %
cleanup 3 %
cleanup 4 %
cleanup 5 %
User defined signal 2: 31
管道中断(第一条错误消息),但信号处理程序在script.sh
退出之前完成。
如果你想完全忽略SIGPIPE
,你需要为信号添加一个虚拟处理程序,并将主echo
语句的 STDERR 重定向到/dev/null
:
function signalHandler2() {
:
}
trap signalHandler SIGUSR2
trap signalHandler2 SIGPIPE
# ...skip...
for i in {1..100000}; do
echo "doing stuff $i %" 2>/dev/null