考虑使用自定义命令行软件(例如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。
该特定示例的另一个选项是使用zsh
orksh93
代替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