stdinが閉じるまで繰り返されるshスクリプト - これをどのようにテストしますか?

stdinが閉じるまで繰り返されるshスクリプト - これをどのようにテストしますか?

入力ストリームをパイプしようとしていますが、ダウンストリームパイプラインプログラムが失敗する可能性がある状況を捉えたいと思います。この場合は再起動する必要があります。だから私はそれをループに入れました。

step1 |while true ; do step2 ; done

しかし、アップストリームパイプを閉じると、ループが終了することを望みます。ステップ2の終了状態ではこれは不明です。それ以外の場合は、次のように言った可能性があります。

step1 |until [ $? -ne 0 ] ; do step2 ; done

stdinが閉じていることを確認しますが、実際にstdinの文字を使用しないテストが必要です。

Cではどうするのか気になります。バッファがゼロのread(2)を使用してこれをテストできますか?私はそうは思わない。オプション(2)はどうですか?いいえ。それともそうでしょうか? fcntl(2) はどうですか?何も見つかりません。

バイトを消費して何とか再出力する以外に、本当に他の方法はありませんか?ファイル記述子を閉じずにテストしますか?

答え1

少なくともLinuxでは、poll()イベントマスクでwithを使用して、パイプのもう一方の端が閉じられているかどうかを知ることができます。POLLHUP

ただし、この時点で読み取るデータがパイプに残っている可能性があるため、そのデータも確認することをお勧めします。 Linuxでは、ioctlを介してこれを行うことができますFIONREAD

したがって、次のいずれかを定義できます。

stdin_alive() {
  perl -MIO::Poll -e '
    require "sys/ioctl.ph";
    -p STDIN or die "stdin is not a pipe\n";
    $p = IO::Poll->new;
    $p->mask(STDIN, POLLHUP);
    if ($p->poll(0)) {
      ioctl(STDIN, &FIONREAD, $n) or die "FIONREAD: $!\n";
      $n = unpack "L", $n;
      exit 1 unless $n;
    }'
}

次のように使用してください。

step1 | while stdin_alive; do step2; done

ifne別の方法は、次のコマンドを使用することですmoreutils

step1 |
  while
    ifne sh -c 'step2 && exit 42'
    [ "$?" -eq 42 ]
  do
    continue
  done

ifne標準入力をお読みください。少なくとも1バイトを読み取ると、コマンドifneが開始され、対応する標準入力が新しいパイプに接続され、その新しいパイプを介して標準入力から読み取られた内容がコマンドに挿入されるため、追加のシャベルが発生し、効率が低下します。転送用の追加のパイプがありますが、これは入力タイプ(パイプだけでなく)に関係なく機能することを意味します。

以前の解決策とのもう1つの違いは、ifne開始前にstdinへの入力を待ちますstep2が、poll()+FIONREADメソッドはパイプがアクティブであるかどうか、チェック時点の正確な時点にデータがあることを確認します。ただし、POLLINこれはポーリングされるイベントのリストに追加することで変更できます。

関連情報