Ctrl + C を検出してシャットダウンするカスタム コマンド ライン ソフトウェア (例: loopHelloWorld) を検討しています。 答えを失わずにパイプするにはどうすればよいでしょうか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 が送信されます (それらはすべて、対話型シェルのフォアグラウンド ジョブに対応する同じプロセス グループで実行されるため)。
つまり:
loopHelloWorld |
while IFS= read -r line; do
echo "$line"
done
実行中のプロセスloopHelloWorld
と、ループを実行するサブシェルを実行しているプロセスの両方while
が を取得しますSIGINT
。
loopHelloWorld
メッセージをstdoutに書き込むとCtrl+C to nicely shutdown
、パイプにも書き込まれます。後もう一方の端のサブシェルがすでに死んでいる場合はloopHelloWorld
、またSIGPIPE を受信し、これを処理する必要があります。
ここでは、そのメッセージはコマンドの通常の出力ではないため (ping
ただし、この例には適用されません)、stderr に書き込む必要があります。そうすると、パイプを通過しなくなります。
または、while ループを実行しているサブシェルで SIGINT を無視して、loopHelloWorld
SIGINT 後の出力を読み取り続けることもできます。
loopHelloWorld | (
trap '' INT
while IFS= read -r line; do
printf '%s\n' "$line"
done
)
ただし、 を押すとパイプラインの終了ステータスが 0 になりますCtrl+C。
この特定の例の別のオプションは、 の代わりにzsh
または を使用することです。これらのシェルでは、ループはメイン シェル プロセスで実行されるため、SIGINT の影響を受けません。ksh93
bash
while
ただし、フォアグラウンド プロセス グループで実行する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