2015-12-17 12 views
12

bashプロセス置換を使用して、ファイルに対して2つの異なるコマンドを同時に実行したいとします。この例では、必ずしも必要ではありませんが、 "cat/usr/share/dict/words"は50GBのファイルを解凍するなどの非常に高価な操作でした。bashプロセスの置換とテールの結果が正しくありませんか?

cat /usr/share/dict/words | tee >(head -1 > h.txt) >(tail -1 > t.txt) > /dev/null 

このコマンドの後、私はh.txtは、「A」を提出し、t.txtファイルの最後の行「Zyzzogeton」を含むように単語の最初の行が含まれていることを期待します。

しかし実際には、h.txtには "A"が含まれていますが、t.txtには "argillaceo"が含まれており、ファイルには約5%含まれています。

どうしてですか? 「テール」プロセスが早期に終了しているか、ストリームが混ざっているようです。 z.txtはすべてが含まれている間、私はA.TXTが「A」で始まるすべての単語を含むことを期待したい。このコマンドの後

cat /usr/share/dict/words | tee >(grep ^a > a.txt) >(grep ^z > z.txt) > /dev/null 

:このような他の同様のコマンドを実行

期待通りに動作します「z」で始まる言葉は、まさに起こったことです。

これはなぜ "tail"で動作しないのですか?これ以外のコマンドではうまくいきませんか?

+1

別のハックのオプションは、それが非ゼロの長さになるまでheadプロセスリストの仕上げをさせて開始する前にt.txtをゼロにすることです/ questions/4489139/bash-process-substitution-and-syncingは、外部コマンドが終了すると直ちに置換プロセスが終了することを示唆していますが、率直に言って、これは現在の問題です。 –

答えて

10

[OK]を、何が起こるように思われることはhead -1コマンドが終了後にそれが出ていることで、それはそれはEPIPEを生成プロセス置換設定名前付きパイプに書き込もうとSIGPIPEを取得するためにteeを引き起こし、man 2 writeに従って意志も書き込みプロセスでSIGPIPEを生成すると、teeが終了し、それによってtail -1がすぐに終了し、左側のcatSIGPIPEになります。

我々はheadとプロセスにもう少しを追加し、出力は両方とも、より予測可能でもteeに頼らずstderrに書き込ませる場合我々は、これは少し良く見ることができます:

I
for i in {1..30}; do echo "$i"; echo "$i" >&2; sleep 1; done | tee >(head -1 > h.txt; echo "Head done") >(tail -1 > t.txt) >/dev/null 

すべてが終了する前にt.txtはまだのみ1を持っているけれどもそれは(ループのちょうど1より多くの反復を得た

1 
Head done 
2 

:私に出力を与え、それを実行します その中に)。私たちは、その後

echo "${PIPESTATUS[@]}" 

をした場合、私たちは私たちがここで見ているものに非常によく似た方法でSIGPIPEからthis question

141 141 

を参照してください。

coreutilsの保守担当者は、将来の後継のために例としてtee "gotchas"にこれを追加しました。これはあなたがGNUバージョン8へのアクセス権を持っている場合は、http://debbugs.gnu.org/cgi/bugreport.cgi?bug=22195

で(閉鎖notabug)のレポートを見ることができますPOSIX準拠にどのように適合するかについての開発者との議論については

。24彼らは -p--output-error=warnのようないくつかのオプション(POSIXではなく)を追加しました。それがなければ、あなたは、リスクのビットを取ることができますが、SIGPIPEをトラップし、無視することによって問題の所望の機能性を得る:

trap '' PIPE 
for i in {1..30}; do echo "$i"; echo "$i" >&2; sleep 1; done | tee >(head -1 > h.txt; echo "Head done") >(tail -1 > t.txt) >/dev/null 
trap - PIPE 

h.txtt.txtの両方で期待通りの結果を持っていますが、何か他のものはSIGPIPEを望んでいたことが起こった場合には正しく処理されるためには、このアプローチでは不運になります。私はこれがhttp://stackoverflow.comに関連していると思います

> t.txt; for i in {1..10}; do echo "$i"; echo "$i" >&2; sleep 1; done | tee >(head -1 > h.txt; echo "Head done"; while [ ! -s t.txt ]; do sleep 1; done) >(tail -1 > t.txt; date) >/dev/null 
+1

'tee'のPOSIX指定の動作は、そのリーダが終了しても動作を継続するためのものです。逆のことがある場合は、実際にはバグです。 –

+0

"正常にオープンしたファイルオペランドへの書き込みが失敗した場合、他の正常にオープンされたファイルオペランドおよび標準出力への書き込みは続行されますが、終了ステータスはゼロではありません。 - http://pubs.opengroup.org/onlinepubs/9699919799/utilities/tee.html –

+0

@CharlesDuffy上記の結果は古いバージョンのものですが、8.5と再試行できます。また、プロセスの置換がファイルハンドルとして近いかどうか、またはプロセスが終了したときにSIGPIPEを実際に生成するかどうかを知るのに十分な深さを掘り下げなかった。バグレポートを提出する前にもっと多くの作業が必要だろうと思う。 –

関連する問題