bash: パイプ、ティー、プロセス代替の競合条件

bash: パイプ、ティー、プロセス代替の競合条件

twice出力を2回見たいのですが、このスクリプトは一度だけ出力します。

dump() {
    (sleep 1; cat) > "$1"
}
(sleep 0; echo "twice") | tee >(dump "./a.txt")
echo "$(< "a.txt")"

2回視聴するには、睡眠スケジュールを調整する必要がありました。

dump() {
    (sleep 0; cat) > "$1"
}
(sleep 1; echo "twice") | tee >(dump "./a.txt")
echo "$(< "a.txt")"

ここで競合状態の原因は何ですか?

答え1

プロセス交換呼び出しはdump非同期プロセスとして実行されます。これはtee、出力がここに書き込まれ、パイプラインが完了したことを意味します。tee作成すると出力がバッファリングされ、パイプが完了します。データ量がパイプバッファサイズを超えています。、使用するのをtee待つ必要があり、元のコードはほとんど機能します。dump

質問で行ったように少量のデータしか書き込めないと仮定すると、a.txtパイプが終了した直後にファイルに何も書き込む機会が生じる前にデータを読み取りますdump(データが保留中の状態でまだバックグラウンドで眠っています)。パイプバッファ)。

エラーコードを実行した後にファイルを見ると、a.txt文字列が含まれていることがわかりますtwice。したがって、sleep 1関数に与えられたわずかな遅延の後、最終的にはその位置に到達します。

パイプが早期にシャットダウンするのを防ぐには、cat最後に次を追加します。

dump() {
    (sleep 1; cat) > "$1"
}

(sleep 0; echo "twice") | tee >(dump "./a.txt") | cat
echo "$(< "a.txt")"

catこれで、プロセスは出力がパイプを介して到着するのを待たなければならないために機能しますdump(そうではありませんが、それはわかりません)。これにより、dump呼び出しが返されるまでパイプのシャットダウンが遅れます。この時点でデータが記録され、a.txtスクリプトの最後のコマンドを使用して取得できます。

パイプラインでプロセスを同期する唯一のものはI / Oです。これは、前のプロセスからデータを読み取り、それを次のプロセスに書き込むことです。プロセスがある時点で前のステップのデータを読み取ろうとすると、読み取るデータがあるか、前のステップでパイプの端を閉じるまでブロックされます。

catデフォルトは標準入力から読み取ることです。追加された標準入力は、catパイプラインの前の段階で標準出力teeと手順の置き換えに接続されます。dumpユーティリティcatは、読み取る内容がなくなるまで読み込みます。これはteeどちらも実行が完了するまでdump発生しません。


コードの整理されたバージョン:

dump() {
    sleep 1
    cat >"$1"
}

echo twice | tee >(dump ./a.txt) | cat

cat a.txt

答え2

(sleep 0; echo "twice") | tee >(dump "./a.txt")
echo "$(< "a.txt")"

IIUCの問題は、次の行でコマンド置換を>(...)実行する前に内部プロセスが完了するのを待つ方法です。$(...)

答えはこれを行う良い方法がないということです。システムがこの/dev/fd/メカニズムをサポートしている場合は、次のトリックを使用できますexec fd> >(...)

dump() {
    (sleep 1; cat) > "$1"
}
echo twice | { exec 7> >(dump a.txt); tee /dev/fd/7; exec 7>&-; wait; }
echo "$(< "a.txt")"

はい、見栄えが悪いかもしれません。

echo twice | { a=>(dump a.txt); tee "$a"; eval "exec ${a##*/}>&-"; wait; }

この目的で収集できるためㅏ)最新バージョンのbash(> = 5.0)を使用すると、wait内部でプロセスを実行できます。>(...)雨)/dev/fd/63これらのプロセスは、標準入力からEOFを取得するまで終了しない可能性があり、これはパイプ(または同様の>(...)拡張パイプ)のもう一方の端を閉じるまで発生しません。後者は正しく実行するのが難しいです。

答え3

一部のBashバージョンには、生成されたプロセスを置き換えるプロセスを正しく待つのを防ぐバグがあります。

関連情報