BASHスクリプトのstdinでEOFを検出する方法は?

BASHスクリプトのstdinでEOFを検出する方法は?

私のスクリプトには、stdinから固定サイズのチャンクからデータを読み込み、さらに処理するためにこのデータを一度に1つずつ外部プログラムに送信する必要があるbash関数があります。関数自体はデータがある限りループで実行する必要があります(入力は常に整数の塊として保証されています)、データを解釈する必要がないため、関数の標準入力でEOFを検出する方法が必要です。処理するデータがまだ残っている場合は、データを消費します。

明らかにこれを行う自然な方法は、read次の組み込み関数を使用することです。

while read -r -n 0 ; do external_program ; done

オプションはほとんどの改行文字ではなく、最大このバイトのみを読み取るように-n指示しますreadが、残念ながらゼロバイトでは機能しないため、EOFの理想的なテストになります。で動作します-n 1が、外部プログラムに入るストリームとして「再生」する必要があるチャンクの最初のバイトを消費します。

それでは、bash組み込み機能を使用するよりも良い方法がありますか?

ベストアンサー1

実際、ゼロ以外のバイト数を読み取ろうとせずにEOFを検出できるかどうかはわかりません。

read()システムコールには戻り値がないためです。明らかにファイルの終わりを示します。代わりに取得できるのは、「読み取ったバイトがゼロでエラーなし」であり、これが何を意味するのかを知るのはアプリケーションコードによって異なります。通常のファイルでは、ファイルの終わりから読んだり、ファイルの終わりを超えて読んだりしても、データが残っていないときに明らかにこれが起こります。

ただし、端末では、ユーザーが空白行で ^D を押して端末インタフェースがそのポイントのコンテンツを返すため、これが発生する可能性があります。これはデータグラムソケットでは何もなく、長さゼロの情報を送受信することが可能です。 。これらの場合のどれも実際の終了を示さない。端末は^ D以降のデータを読み取ることができ、ソケットは長さ0のメッセージの後に追加のメッセージを受け取ることができます。 (通常のファイルでもファイルに同時に別のプロセスが関連付けられている場合は、後続のファイルからデータが返されることがあります。EOFを繰り返し読み込むのが最も簡単な実装ですtail -f。)

ゼロバイトを読み取るように明示的に要求すると、EOF状態であるかどうかにかかわらず、ゼロバイト(またはエラー)が発生します。


外部プログラムが大きな問題なくEOFを処理できる場合は、それを示す終了コードを返すだけで最良の結果が得られます。これにより、次のことができます。

while external_program; do
    # do we need to do anything here but loop?
    true 
done

あるいは、運が良ければ、他のEOF終了状態を得ることもできます。

while true; do
    external_program
    ret=$?
    if [ "$ret" = 0 ]; then
        echo "ok, continue"
    elif [ "$ret" = 1 ]; then
        echo "deal with this error"
        # but what now?
    elif [ "$ret" = 2 ]; then
        echo "got EOF, stopping"
        break
    fi
done

プログラムが受信した入力を検証する必要があるため、EOFを処理することが合理的です。

そうでない場合は、Bashにデータの塊を読み込み、それをプログラムに渡すことができます(実際に十分なデータを読み込んだ場合)。

blocksize=123
while IFS= read -d '' -r -n "$blocksize" data && [ "${#data}" = "$blocksize"]; do
    printf "%s" "$data" | externalprogram
done

ただし、これはデータにNULバイト(\0)が含まれていない場合にのみBashで機能します。もしそうなら、Zsh(またはいくつかの実際のプログラミング言語)に切り替えるか、同様のhead -c "$blocksize" > tmpfile

おすすめ記事