
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バージョンには、生成されたプロセスを置き換えるプロセスを正しく待つのを防ぐバグがあります。