向命令管道发送信号拆除整个管道

Gab*_*iel 2 pipe signals

我有一个两个过程的简单示例

第一个是一个简单的循环,它进行一些处理

#!/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
Run Code Online (Sandbox Code Playgroud)

我像这样启动了脚本:

./script.sh | grep "wow"
Run Code Online (Sandbox Code Playgroud)

我附加了一个辅助 grep 命令只是为了演示。

grep当我仅向进程 ( )发送 USR2 信号时kill USR2 $(pgrep grep),整个管道就会被拆除 (?)。为什么script.sh不继续了?

其次,当我向整个进程组发送 USR2 信号(类似于kill USR2 -$!)时,显然会发生同样的情况,因为grep退出速度很快,它会破坏整个管道而不让script.sh执行signalHandler. 我想我有一些误解,这应该如何运作?

多谢

Gui*_*ido 5

正如 @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   # <--
Run Code Online (Sandbox Code Playgroud)

为了使示例简单,使用相同的信号处理程序来处理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
Run Code Online (Sandbox Code Playgroud)

管道中断(第一条错误消息),但信号处理程序在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
Run Code Online (Sandbox Code Playgroud)