IOError:损坏的pipe道: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。
我redirect输出,所以如果上面的脚本被命名为“open.py”,那么我的命令运行将是:
open.py | othercommand
我没有转载这个问题,但也许这个方法可以解决这个问题:(逐行写入stdout
而不是使用print
)
import sys with open('a.txt', 'r') as f1: for line in f1: sys.stdout.write(line)
你可以抓到破pipe子? 这将文件写入stdout
,直到pipe道closures。
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
在pipe道读取太大之前正在读取 – https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer
问题是由于SIGPIPE处理。 您可以使用以下代码解决此问题:
from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE,SIG_DFL)
在这里看到这个解决scheme的背景。
为了给Alex L.提供有用的答案 , akhan的有用答案 ,以及Blckknght的有用答案和一些额外的信息:
-
标准的Unix信号
SIGPIPE
被发送到一个进程写入一个pipe道,当没有进程从pipe道读取 (了)。- 这不一定是一个错误的条件; 一些诸如
head
devise的 Unix实用程序一旦收到足够的数据就会停止从pipe道中过早地读取数据。
- 这不一定是一个错误的条件; 一些诸如
-
默认情况下 – 即, 如果写入过程没有显式地捕获
SIGPIPE
– 写入过程被简单地终止 ,并且其退出代码被设置为141
,其被计算为128
(通常用信号终止信号)+13
(SIGPIPE
'具体的信号编号 )。 -
然而,通过devise, Python 本身会捕获
SIGPIPE
, 并将其转换为具有errno
值errno.EPIPE
的PythonIOError
实例,以便Python脚本可以捕获它,如果它select的话,请参阅Alex L.关于如何实现的答案 。 -
如果一个Python 脚本 没有捕获它 ,Python会输出错误消息
IOError: [Errno 32] Broken pipe
, 终止脚本,退出代码1
– 这是OP看到的症状。 -
在许多情况下,这比破坏性更有帮助 ,因此恢复到默认行为是可取的 :
-
正如阿汗的答案所述,使用
signal
模块就可以实现这一点。signal.signal()
需要一个信号来处理第一个参数和一个处理程序作为第二个参数; 特殊处理程序值SIG_DFL
表示系统的默认行为:from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE, SIG_DFL)
-
当您尝试写入已在另一端closures的pipe道时,会发生“断开的pipe道”错误。 由于你所展示的代码并不直接涉及到任何pipe道,我怀疑你正在做一些Python之外的事情来将Python解释器的标准输出redirect到别的地方。 如果你正在运行这样的脚本,可能会发生这种情况:
python foo.py | someothercommand
你有的问题是,其他命令正在退出,没有读取其标准input上可用的所有东西。 这会导致您的写入(通过print
)在某个时候失败。
我能够在Linux系统上使用以下命令重现错误:
python -c 'for i in range(1000): print i' | less
如果我closuresless
分页器而不滚动其所有input(1000行),Python将退出,并报告相同的IOError
。
我觉得有必要指出使用的方法
signal(SIGPIPE, SIG_DFL)
确实是危险的 (正如David Bennet在评论中已经提到的那样),在我的情况下,当与multiprocessing.Manager
(由于标准库依赖BrokenPipeError在几个地方被引发)相结合导致依赖于平台的有趣业务。 为了使一个漫长而痛苦的故事短,这是我如何解决它:
首先,您需要捕获IOError
(Python 2)或BrokenPipeError
(Python 3)。 根据你的程序,你可以尝试在这个时候提前退出,或者忽略这个exception:
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
不幸的是摆脱这个消息不是直截了当,但我终于find了罗伯特·柯林斯在这里build议这个解决方法,我变成了一个装饰器,你可以包装你的主要function(是的,这是一些疯狂的地方) http://bugs.python.org/issue11380缩进):;
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脚本,这对我来说很可爱。
closures应按照打开的相反顺序进行。