web-dev-qa-db-ja.com

Broken Pipeエラーを修正するにはどうすればいいですか?

SSDドライブを入手したときにUbuntu 12.10を新規インストールした後、最近 http://rvm.io の指示に従って)RVMを再インストールしました。

さて、私が入力したとき:type rvm | head -1

次のようなエラーが表示されます。

rvm is a function
-bash: type: write error: Broken pipe

しかし、私がすぐにコマンドを繰り返すならば、私は受け取るだけです:

rvm is a function

そしてそれはすべて大丈夫のようですか?何が起こっていますか?修正するにはどうすればよいですか。いつも起こるわけではありません。それはもっと散発的であるようです。私はそれに何らかの種類のパターンを見つけようとしましたが、まだしていません。

33
Jason Shultz

このような状況で「破損したパイプ」が表示されることはまれですが、通常のことです。

type rvm | head -1を実行すると、bashはあるプロセスでtype rvmを実行し、別のプロセスでhead -1を実行します。1 typeの標準出力はパイプの「書き込み」端に接続され、headの標準入力は「読み取り」端に接続されます。両方のプロセスが同時に実行されます。

head -1プロセスはstdinからデータを(通常は8kBの塊で)読み込み、(-1オプションに従って)1行を出力して終了し、パイプの「読み込み」の終わりを閉じます。 rvm関数は非常に長いので(bashによって解析され再構築された後で約11kB)、これはheadがまだ書き出すべき数kBのデータを持っている間にtypeが終了することを意味します。

この時点で、typeはもう一方の端が閉じられているパイプに書き込もうとしているので - 壊れたパイプ - それが呼び出したwrite()関数は"Broken pipe"として翻訳されたEPIPEエラーを返します。このエラーに加えて、カーネルはSIGPIPEシグナルをtypeにも送信します。これはデフォルトでプロセスを即座に強制終了します。

(このシグナルは対話型シェルでは非常に役に立ちます。ほとんどのユーザーは最初のプロセスを実行してどこにも書き込もうとしないためです。一方、非対話型サービスはSIGPIPEを無視します。そのような単純なエラーで死ぬ - それで彼らはエラーコードを非常に役に立つと思う。)

ただし、シグナルの配信は100%即時ではないため、write()がEPIPEを返し、シグナルを受信する前にプロセスが短時間実行され続ける場合があります。この場合、typeは失敗した書き込みに気付き、エラーコードを翻訳し、SIGPIPEによって強制終了される前にエラーメッセージをstderrに出力するのに十分な時間を得ます。 (typeはbash自体の組み込みコマンドなので、エラーメッセージには "-bash:type:"と書かれています。)

typeプロセスとカーネルのシグナルデリバリーコードは文字通り同時に異なるコア上で実行できるため、これはマルチCPUシステムではより一般的なようです。

Write()関数からEPIPEを受け取るとすぐに終了するように(bashのソースコード内の)typeビルトインにパッチを当ててこのメッセージを削除することは可能です。

しかし、気にする必要はなく、rvmのインストールとはまったく関係ありません。

55
grawity

このように、パイプにtail -n +1を挿入することで、壊れたパイプを別のプロセスを犠牲にして修正できます。

タイプrvm | tail -n + 1 |頭-1

+1は、入力の最初の行とそれに続くすべての行を出力するようにtailに指示します。出力はtail -n +1が存在しない場合とまったく同じになりますが、プログラムは標準出力をチェックするのに十分スマートで、パイプをきれいに閉じます。これ以上壊れたパイプはありません。

23
Huuu

write error: Broken pipeメッセージは、そのパイプの読み込み側にリーダーが残っていない状態でパイプに書き込もうとする書き込みプロセス、およびSIGPIPEシグナルが現在のプロセスまたは親プロセスによって無視されるように設定されているという特別な状況を示します。 SIGPIPEを無視するように設定したのが親プロセスだった場合、子プロセスが非対話型シェルでそれを元に戻すことはできません。

ただし、明示的なサブシェルを使用してtype rvmが終了したときにhead -1を強制終了することは可能です。このようにしてtype rvmをバックグラウンド化し、typepidhead -1サブシェルに送り、そこでEXITにトラップを実装してtype rvmを明示的に殺すことができます。

trap "" PIPE        # parent process sets SIGPIPE to be ignored
bash                # start child process
export LANG=C
# create a fake rvm function
eval "
rvm() {
$(printf 'echo line of rvm code %s\n' {1..10000})
}
"

# rvm is a function
# bash: type: write error: Broken pipe
type rvm | head -1

# kill type rvm when head -1 terminates
# sleep 0: do nothing but with external command
( (sleep 0; type rvm) & echo ${!} ; wait ${!} ) | 
    (trap 'trap - EXIT; kill "$typepid"; exit' EXIT; typepid="$(head -1)"; head -1)
2
zancox