2012-01-23 1 views
22

私はbashで複数のコマンドのパイプを使用しています。コマンドの1つが失敗するとすぐに、パイプライン全体ですべてのコマンドを終了するようにbashを設定する方法はありますか?シェルパイプ:1つのコマンドが失敗したときにすぐに終了します。

私の場合、最初のコマンド、たとえばcommand1は、何らかの出力が出るまでしばらくの間実行されます。たとえば、command1(sleep 5 && echo "Hello")に置き換えることができます。

今すぐcommand1 | falseは5秒後に失敗しますが、すぐには失敗しません。

この動作には、コマンドが生成する出力の量と関係があるようです。たとえば、find/| falseはすぐに戻ります。

一般に、なぜbashがこのように動作するのだろうと思います。 command1 | non-existing-commandのようなコードが一度に終了しないと便利な状況を想像できますか?

PS:中間ファイルを使用すると、中間結果が保存されるため、一時ファイルを使用することはできません。

PPS:set -eset -o pipefailもこの現象に影響しないようです。

+1

この質問は、http://unix.stackexchange.comにより適しています。あなたはおそらくそこに良い答えを得るでしょう。 – dogbane

答えて

16

bashのドキュメントそのsection about pipelinesで述べている:「独自のサブシェルで」

パイプライン内の各コマンドは、独自のサブシェルで実行されて[...]

は、新しいことを意味しbashプロセスが生成され、実際のコマンドが実行されます。各サブシェルは、実行するように要求されたコマンドが存在しないとすぐに判断した場合でも、正常に起動します。

これは、コマンドの1つがナンセンスであってもパイプ全体を正常にセットアップできる理由を説明しています。 Bashは各コマンドを実行できるかどうかを確認せず、サブシェルに委譲します。これはまた、例えば、コマンドnonexisting-command | touch helloが "コマンドが見つかりません"というエラーを出すが、それにもかかわらずファイルhelloが生成される理由を説明する。

同じセク​​ションでは、それはまた、言う:パイプライン内のすべてのコマンドのため

シェル待機値を返す前に終了します。 sleep 5 | nonexisting-command

A。H.が指摘したように、sleep 5は5秒後に終了し、すぐに、それ故にシェルも5  秒間待機しますありません。

なぜ実装がこのように行われたのかわかりません。あなたのようなケースでは、その行動は確かに予想通りではありません。

とにかく、1つの少し醜い回避策は、FIFOを使用することです。ここでは

mkfifo myfifo 
./long-running-script.sh > myfifo & 
whoops-a-typo < myfifo 

long-running-script.shを開始し、その後、スクリプトは次の行にすぐに失敗しています。複数のFIFOを使用すると、これを2つ以上のコマンドでパイプに拡張できます。

4

sleep 5は完了するまで出力を生成しませんが、find /はすぐに出力を生成し、bashはfalseにパイプします。

+0

真実ですが、問題は良いですが、あまり選択されていない例があります。 –

+0

@Jaypal:この例は誤解を招くかもしれないと私は同意します。私はその投稿を編集し、今はっきりしていることを願っています。 – Tobi

+0

@ダン:はい、これは本当に私の質問に答えるものではありません。私は、1つのコマンドが失敗したら、パイプラインですべてのコマンドを終了させることができるかどうかを知りたい。 – Tobi

2

find/|falsewrite(2)findからのシステムコールがエラーEPIPE(破損パイプ)で失敗するため、より高速に処理が失敗します。これは、falseが既に終了しているため、これらの2つのコマンドの間のパイプが既に一方の側で閉じられているためです。

findがそのエラーを無視すると(理論的にはそうすることができる)、それは「遅くなる」こともあります。

(sleep 5 && echo "Hello") | falseは、最初の部分であるsleepがパイプを書き込みによって「テスト」しないため、「遅い」となります。 5秒後にechoにもEPIPEエラーが発生します。このエラーがこの場合の最初の部分を終了するかどうかは、質問にとって重要ではありません。

3

パイプに日付を書き込もうとするまで、最初のプログラムは2番目のプログラムが終了しているかどうかを認識しません。秒が終了すると、最初にSIGPIPEが受信され、通常はすぐに終了します。

あなたはこのように、凝視した直後にパイプさ出力の最初の行を強制することができます。

(sleep 0.1; echo; command1) | command2 

この100msの睡眠は、右の開始後に可能command2の終了まで待つことを意図しています。 もちろん、command2が2秒後に終了し、command1が60秒間サイレントになると、シェルコマンド全体が60.1秒後にのみ戻されます。

関連する問題