如何通过管道传输 bash 命令并保持 Ctrl+C 工作?

如何通过管道传输 bash 命令并保持 Ctrl+C 工作?

考虑使用自定义命令行软件(例如loopHelloWorld)来检测ctrl+C 以实现良好的关闭。如何在不丢失的情况下通过管道传递答案Ctrl+C

$ loopHelloWorld
    - Ctrl+C to nicely shutdown

但是使用管道时,管道会在没有很好地关闭的情况下杀死软件

$ loopHelloWorld | 
    while IFS= read -r line; do
        echo "$line"
    done

例子

ping example.com |
  while IFS= read -r line; do
    echo "$line"
  done

答案1

Ctrl+C导致 SIGINT 被发送到管道中的所有进程(因为它们都在与交互式 shell 的前台作业相对应的同一进程组中运行)。

所以在:

loopHelloWorld | 
  while IFS= read -r line; do
    echo "$line"
  done

正在运行的进程loopHelloWorld和运行循环的子 shell 的while进程都会获得SIGINT.

如果将消息loopHelloWorld写入Ctrl+C to nicely shutdown其标准输出,它也会被写入管道。如果那是另一端的子 shell 已经死亡,那么loopHelloWorld将会收到一个 SIGPIPE,您需要处理它。

在这里,您应该将该消息写入 stderr,因为它不是命令的正常输出(但不适用于该ping示例)。那么它就不会通过管道。

或者您可以让运行 while 循环的子 shell 忽略 SIGINT,以便它loopHelloWorld在 SIGINT 之后继续读取输出:

loopHelloWorld | (
  trap '' INT
  while IFS= read -r line; do
    printf '%s\n' "$line"
  done
)

然而,当您按 时,这会导致管道的退出状态为 0 Ctrl+C

该特定示例的另一个选项是使用zshorksh93代替bash。在这些 shell 中,while循环将在主 shell 进程中运行,因此不会受到 SIGINT 的影响。

loopHelloWorld | cat这对于在前台进程组中运行cat并没有帮助。loopHelloWorld

答案2

使用陷阱:

#! /bin/bash

trap handleInt SIGINT

interrupted=0

function handleInt {
    echo "Interrupted..."
    interrupted=1
}


while true
do
    [[ interrupted -ne 0 ]] && { echo "Ctrl-C caught, exiting..." ; exit ; }
    echo "Sleeping..."
    sleep 1
    read -r line
    echo "$line"
done

相关内容