非常に単純な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
私は問題を再現していませんが、おそらくこの方法で解決するでしょう:(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
もう一方の端で閉じられたパイプに書き込もうとすると、「壊れたパイプ」エラーが発生します。示したコードにはパイプが直接含まれていないため、Pythonインタープリターの標準出力を別の場所にリダイレクトするためにPythonの外部で何かをしているのではないでしょうか。これは、次のようなスクリプトを実行している場合に発生する可能性があります。
python foo.py | someothercommand
あなたが持っている問題は、someothercommand
がその標準入力で利用可能なすべてを読み取らずに終了することです。これにより、ある時点で(print
を介した)書き込みが失敗します。
Linuxシステムで次のコマンドを使用してエラーを再現できました。
python -c 'for i in range(1000): print i' | less
入力(1000行)をすべてスクロールせずにless
ページャーを閉じると、Pythonは、報告したのと同じIOError
で終了します。
私はメソッドを使用することを指摘する義務を感じています
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
これは、スクリプトからの出力の読み取り終了が早期に終了した場合にも発生する可能性があります
すなわちopen.py | otherCommand
otherCommandが終了し、open.pyがstdoutに書き込もうとした場合
私はこれを私に素敵なものにする悪いgawkスクリプトを持っていました。
私はこれがそれを行うための「適切な」方法ではないことを知っていますが、単にエラーメッセージを取り除くことに興味があるなら、この回避策を試すことができます:
python your_python_code.py 2> /dev/null | other_command
クローズは、オープンの逆の順序で実行する必要があります。