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 (通常用信号终止信号)+ 13SIGPIPE '具体的信号编号 )。

  • 然而,通过devise, Python 本身会捕获SIGPIPE并将其转换为具有errnoerrno.EPIPE 的Python IOError实例,以便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应按照打开的相反顺序进行。