IOError:[Errno 32]壊れたパイプ:Python


88

私は非常に単純なPython3スクリプトを持っています:

f1 = open('a.txt', 'r')
print(f1.readlines())
f2 = open('b.txt', 'r')
print(f2.readlines())
f3 = open('c.txt', 'r')
print(f3.readlines())
f4 = open('d.txt', 'r')
print(f4.readlines())
f1.close()
f2.close()
f3.close()
f4.close()

しかし、それは常に言います:

IOError: [Errno 32] Broken pipe

これを修正するための複雑な方法をすべてインターネットで見ましたが、このコードを直接コピーしたので、PythonのSIGPIPEではなくコードに問題があると思います。

出力をリダイレクトしているので、上記のスクリプトの名前が「open.py」の場合、実行するコマンドは次のようになります。

open.py | othercommand

@squiguyライン2:print(f1.readlines())
JOHANNES_NYÅTT

2
2行目で2つのIO操作が発生しa.txtていstdoutます。読み取り元と書き込み先です。おそらく、それらを別々の行に分割して、どの操作が例外をトリガーするかを確認してみてください。stdoutがパイプで、読み取り端が閉じている場合は、EPIPEエラーの原因である可能性があります。
James Henstridge 2013年

1
(適切な条件が与えられれば)出力でこのエラーを再現できるので、print呼び出しが原因であると思われます。@JOHANNES_NYÅTT、Pythonスクリプトをどのように起動するかを明確にできますか?標準出力をどこかにリダイレクトしていますか?
Blckknght 2013年

2
これは、次の質問が重複する可能性である:stackoverflow.com/questions/11423225/...

回答:


45

私は問題を再現していませんが、おそらくこの方法で解決できるでしょう:(stdout使用するのではなく、行ごとに書き込むprint

import sys
with open('a.txt', 'r') as f1:
    for line in f1:
        sys.stdout.write(line)

壊れたパイプを捕まえることができますか?これstdoutにより、パイプが閉じられるまでファイルが1行ずつ書き込まれます。

import sys, errno
try:
    with open('a.txt', 'r') as f1:
        for line in f1:
            sys.stdout.write(line)
except IOError as e:
    if e.errno == errno.EPIPE:
        # Handle error

またothercommand、パイプが大きくなりすぎる前に、パイプから読み取っていることを確認する必要があります-https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer


7
これは優れたプログラミング手法ですが、質問者が取得しているパイプの破損エラーとは何の関係もないと思います(おそらくprint、ファイルの読み取りではなく、呼び出しに関係しています)。
Blckknght 2013年

@Blckknght私はいくつかの質問と代替方法を追加し、作者からのフィードバックを期待していました。問題が開いているファイルから直接printステートメントに大量のデータを送信することである場合は、おそらく上記の代替方法の1つで修正できます。
Alex L

(多くの場合、最も単純なソリューションが最適です。ファイル全体をロードしてから印刷する特別な理由がない限り、別の方法で実行してください)
Alex L

1
これをトラブルシューティングするのに素晴らしい仕事です!私はこの答えを当然のことと思うことができましたが、他の答え(そして私自身のアプローチ)があなたの答えと比較してどのように薄れているかを見た後でのみこれを理解することができました。
Jesvin Jose 2013

117

この問題は、SIGPIPEの処理が原因です。この問題は、次のコードを使用して解決できます。

from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE,SIG_DFL) 

このソリューションの背景については、こちらご覧くださいここでより良い答え。


13
私が発見したように、これは非常に危険です。ソケット(httplibなど)でSIGPIPEを取得した場合、プログラムは警告やエラーなしで終了するだけだからです。
デビッドベネット

1
@DavidBennett、私はそのアプリケーションに依存していると確信しており、あなたの目的のために受け入れられた答えは正しいものです。ここには、人々が十分な情報に基づいて決定を下すための徹底的なQ&Aがあります。IMO、コマンドラインツールの場合、ほとんどの場合、パイプ信号を無視するのがおそらく最善です。
akhan 2016

1
これを一時的に行う方法はありますか?
ネイトグレン

2
@NateGlenn既存のハンドラーを保存して、後で復元できます。
akhan 2017

3
誰かがブログスポットの記事を公式ドキュメントよりも信頼できる正しい情報源と見なしている理由を誰かに答えてもらえますか(ヒント:リンクを開いて、壊れたパイプのエラーを適切に修正する方法を確認してください)。:)
Yurii Rabeshko 2018

92

持参するアレックスL.の役に立つ答えakhanの役に立つ答え、そしてBlckknghtの役に立つ答えをいくつかの追加情報とともに:

  • 標準のUnixシグナルSIGPIPEは、パイプからのプロセス読み取りがない場合(もはや)、パイプ書き込むプロセスに送信されます。

    • これは必ずしもエラー状態ではありません。head 設計上などの一部のUnixユーティリティは、十分なデータを受信すると、パイプからの読み取りを途中で停止します。
  • デフォルトでは、つまり、書き込みプロセスが明示的にトラップ しない場合、書き込みプロセスSIGPIPEは単に終了し、その終了コードはに設定されます141。これは、128(一般にシグナルによる終了を通知するために)+ 13SIGPIPEの特定の信号番号)として計算されます。。

  • ただし、設計上、Python自体がトラップしSIGPIPE、値を持つPythonIOErrorインスタンスに変換するため、Pythonスクリプトが選択した場合は、それをキャッチできます。その方法については、AlexL。の回答を参照してください。errnoerrno.EPIPE

  • Pythonの場合、スクリプトがないではない、それをキャッチし、Pythonの出力のエラーメッセージをIOError: [Errno 32] Broken pipeし、終了コードでスクリプトを終了します1-これはOPソー症状です。

  • 多くの場合、これは役立つよりも混乱を招くため、デフォルトの動作に戻すことが望ましいです。

    • akhanの回答で述べられているように、signalモジュールを使用すると、まさにそれが可能になりますsignal.signal()最初の引数として処理するシグナルを受け取り、2番目の引数としてハンドラーを受け取ります。特別なハンドラー値SIG_DFLは、システムのデフォルトの動作を表します

      from signal import signal, SIGPIPE, SIG_DFL
      signal(SIGPIPE, SIG_DFL) 
      

32

「壊れたパイプ」エラーは、もう一方の端で閉じられたパイプに書き込もうとすると発生します。あなたが示したコードは直接パイプを含まないので、Pythonインタープリターの標準出力を別の場所にリダイレクトするためにPythonの外部で何かをしているのではないかと思います。これは、次のようなスクリプトを実行している場合に発生する可能性があります。

python foo.py | someothercommand

あなたが抱えている問題はsomeothercommand、標準入力で利用可能なすべてを読み取らずに終了することです。これにより、(を介したprint)書き込みがいずれかの時点で失敗します。

Linuxシステムで次のコマンドを使用してエラーを再現することができました。

python -c 'for i in range(1000): print i' | less

lessすべての入力(1000行)をスクロールせずにページャーを閉じると、PythonIOErrorは報告したものと同じ状態で終了します。


10
はい、これは本当ですが、どうすれば修正できますか?
JOHANNES_NYÅTT

2
修正方法を教えてください。
JOHANNES_NYÅTT

1
@JOHANNES_NYÅTT:ほとんどのUnixライクなシステムでパイプによって提供されるバッファリングのため、小さなファイルで機能する可能性があります。ファイルの内容全体をバッファに書き込むことができれば、他のプログラムがそのデータを読み取らなくてもエラーは発生しません。ただし、書き込みがブロックされた場合(バッファがいっぱいであるため)、他のプログラムが終了すると失敗します。もう一度言うと:他のコマンドは何ですか?Pythonコードだけではこれ以上お手伝いできません(間違ったことをしている部分ではないため)。
Blckknght 2013年

1
ヘッドに配管するときにこの問題が発生しました... 10行の出力後の例外。非常に論理的、それでも予期しない:)
アンドレ・ラズロ

4
@Blckknght:グッド一般的には情報が、再「修正その:と『やっている一部間違ったことをする』:SIGPIPE信号が必ずしも示すものではありません、エラー状態を、いくつかのUnixユーティリティ、特にhead設計により、通常動作時に近いパイプ早い段階で、必要なだけのデータを読み取ったら
mklement0 2015年

20

私はそれを使用する方法を指摘する義務を感じます

signal(SIGPIPE, SIG_DFL) 

確かに危険であり(David Bennetがコメントですでに示唆しているように)、私の場合、組み合わせたときにプラットフォーム依存の面白いビジネスにつながりましたmultiprocessing.Manager(標準ライブラリはBrokenPipeErrorがいくつかの場所で発生することに依存しているため)。長くてつらい話を短くするために、これは私がそれを修正した方法です:

まず、IOError(Python 2)またはBrokenPipeError(Python 3)をキャッチする必要があります。プログラムに応じて、その時点で早期に終了するか、例外を無視することができます。

from errno import EPIPE

try:
    broken_pipe_exception = BrokenPipeError
except NameError:  # Python 2
    broken_pipe_exception = IOError

try:
    YOUR CODE GOES HERE
except broken_pipe_exception as exc:
    if broken_pipe_exception == IOError:
        if exc.errno != EPIPE:
            raise

ただし、これだけでは不十分です。Python 3でも、次のようなメッセージが出力される場合があります。

Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe

残念ながら、そのメッセージを取り除くのは簡単ではありませんが、最終的にhttp://bugs.python.org/issue11380を見つけました。ここで、Robert Collinsは、メイン関数をラップできるデコレータに変えたこの回避策を提案しています(はい、それはクレイジーです)インデント):

from functools import wraps
from sys import exit, stderr, stdout
from traceback import print_exc


def suppress_broken_pipe_msg(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except SystemExit:
            raise
        except:
            print_exc()
            exit(1)
        finally:
            try:
                stdout.flush()
            finally:
                try:
                    stdout.close()
                finally:
                    try:
                        stderr.flush()
                    finally:
                        stderr.close()
    return wrapper


@suppress_broken_pipe_msg
def main():
    YOUR CODE GOES HERE

2
これは私にとってそれを修正するようには見えませんでした。
カイルブリデンスティン2018

私はBrokenPipeError以外を追加した後、それは私の仕事:supress_broken_pipe_msg機能でパス
Rupen B

2

これが「適切な」方法ではないことはわかっていますが、エラーメッセージを取り除くことに興味がある場合は、次の回避策を試すことができます。

python your_python_code.py 2> /dev/null | other_command

2

ここでの一番の答え(if e.errno == errno.EPIPE:)は私にはうまくいきませんでした。私が得た:

AttributeError: 'BrokenPipeError' object has no attribute 'EPIPE'

ただし、特定の書き込みで壊れたパイプを無視するだけであれば、これは機能するはずです。SIGPIPEをトラップするよりも安全だと思います。

try:
    # writing, flushing, whatever goes here
except BrokenPipeError:
    exit( 0 )

壊れたパイプにぶつかった場合、コードが本当に本当に完了したかどうかを判断する必要があることは明らかですが、ほとんどの目的で、通常はそうなると思います。(ファイルハンドルなどを閉じることを忘れないでください)


1

これは、スクリプトからの出力の読み取り側が途中で終了した場合にも発生する可能性があります

すなわちopen.py | otherCommand

otherCommandが終了し、open.pyがstdoutに書き込もうとした場合

私はこれを私に素敵にした悪いgawkスクリプトを持っていました。


2
パイプからの読み取りプロセスが死ぬことは必ずしも重要headではありません。一部のUnixユーティリティは、特に設計上、通常の操作中に必要な量のデータを読み取ったら、パイプを早期に閉じます。ほとんどのCLIは、デフォルトの動作をシステムに委ねるだけです。読み取りプロセスを静かに終了し、終了コードを報告します141(パイプラインの最後のコマンドが全体的な終了コードを決定するため、シェルではすぐにはわかりません)。残念ながら、Pythonのデフォルトの動作は、騒々しく死ぬことです。
mklement0 2015年

-1

クローズは、オープンの逆の順序で実行する必要があります。


4
これは一般的には良い習慣ですが、行わないこと自体は問題ではなく、OPの症状を説明するものでもありません。
mklement0 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.