bashコマンドをパイプしてCtrl + Cを操作し続けるには?

bashコマンドをパイプしてCtrl + Cを操作し続けるには?

シームレスなシャットダウンのために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に書き込まれると、Ctrl+C to nicely shutdownパイプにも書き込まれます。もしそうなら後ろにもう一方の端にあるサブシェルが死んloopHelloWorldだ場合返品SIGPIPEが受信され、それを処理する必要があります。

ここではコマンドの一般的な出力ではないため、stderrにメッセージを作成する必要があります(ただし、このping例では該当しません)。そうすればパイプを通過できなくなります。

あるいは、whileループを実行しているサブシェルにSIGINTを無視して、loopHelloWorldSIGINT以降の出力を読み続けるようにすることもできます。

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

ただし、これを押すとパイプの終了状態がゼロになりますCtrl+C

この特定の例の別のオプションは、代わりにzshまたはを使用することです。これらのシェルでは、ループはデフォルトのシェルプロセスで実行されるため、SIGINTの影響を受けません。ksh93bashwhile

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

関連情報