web-dev-qa-db-ja.com

IOError:[Errno 32]破損したパイプ:Python

非常に単純なPython 3スクリプトがあります。

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
75
JOHANNES_NYÅTT

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

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

37
Alex L

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

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

こちらを参照 このソリューションの背景について。より良い答え ここ

110
akhan

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

python foo.py | someothercommand

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

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

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

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

26
Blckknght

私はメソッドを使用することを指摘する義務を感じています

signal(SIGPIPE, SIG_DFL) 

確かにdangerous(すでにコメントで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/issue1138 ここで、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
18
trehn

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

すなわちopen.py | otherCommand

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

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

1
lkreinitz

私はこれがそれを行うための「適切な」方法ではないことを知っていますが、単にエラーメッセージを取り除くことに興味があるなら、この回避策を試すことができます:

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

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

0
Paul