2013-01-08 75 views
58

我有一个非常简单的Python 3脚本:IO错误:[错误32]破碎的管道:Python的

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 
+2

什么行号? – squiguy

+0

@squiguy第2行:'print(f1.readlines())' –

+2

第2行有两个IO操作:从'a.txt'读取并写入'stdout'。也许尝试将它们分成不同的行,以便您可以看到哪个操作会触发异常。如果'stdout'是一个管道并且读取结束已经关闭,那么可能会导致'EPIPE'错误。 –

回答

22

我没有复制问题,但也许这种方法将解决这个问题:(写一行行至stdout,而不是使用print

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

你可以赶上破裂的管道?这会将文件逐行写入stdout,直到管道关闭。

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

+5

虽然这是一个很好的编程习惯,但我认为它与提问者得到的破损管道错误没有任何关系(这可能与'print'调用有关,而不是读取文件) 。 – Blckknght

+0

@Blckknght我添加了几个问题和替代方法,希望得到作者的一些反馈。如果问题是从打开的文件直接向打印语句发送大量数据,那么上面的替代方法可能会解决它。 –

+0

(最简单的解决方案往往是最好的 - 除非有一个特殊的理由加载整个文件,然后打印它,做一个不同的方式) –

23

当您尝试写入已关闭的管道时,会发生“断管”的错误另一端。由于你所展示的代码并不直接涉及任何管道,我怀疑你正在做一些Python之外的事情来将Python解释器的标准输出重定向到其他地方。如果你正在运行一个脚本,这样就会出现这种情况:

python foo.py | someothercommand 

你的问题是,someothercommand是不读的标准输入提供一切退出。这会导致您的写入(通过print)在某个时候失败。

我能够重现误差与Linux系统上下面的命令:

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

如果我关闭less寻呼机,而不通过其所有的输入(1000线)的滚动,Python的离开与同IOError你已经报告过。

+5

是的,这是真的,但我该如何解决? –

+0

@JOHANNES_NYÅTT:这可能不是您可以在Python程序中修复的东西。相反,你需要找出为什么其他程序正在退出并修复它(通过更改它的代码,如果可以的话,或者以不同的方式调用它)。我们可能可以提供帮助,但是您需要告诉我们“othercommand”是什么,以及您期望它如何处理您发送的输入。 – Blckknght

+1

请让我知道如何解决它。 –

95

问题是由于SIGPIPE处理。您可以使用下面的代码解决这个问题:

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

See here有关此解决方案的背景。

+1

谢谢@akhan,您的解决方案解决了我的问题,而其他解决方案不能解决问题。我将我的python程序的输出管道输送到(。 /my_program.py | head) – Morlock

+3

我不知道这是为什么,但它是我的英雄 –

+0

可以确认:有效 –

1

如果从脚本输出的读端过早死亡

即open.py这也可能发生| otherCommand

如果otherCommand出口和open.py尝试写入到stdout

我有没有这个可爱的给我一个坏的GAWK脚本。

+0

这不是关于从管道读取进程_dying_,必然:在正常操作期间,一些Unix实用程序,特别是'head',__一旦他们已经读取了尽可能多的数据,就像_they_所需的一样。大多数CLI只是遵循系统的默认行为:静静地终止读取过程并报告退出代码'141'(由于流水线的_last_命令决定了整体退出代码,所以在shell中不太明显)。不幸的是_Python_的默认行为是_noisily_。 – mklement0

0

关闭应按照打开的相反顺序完成。

+0

虽然这是一般的良好习惯,但不做不是问题本身,也不能解释OP的症状。 – mklement0

54

要使用一些额外的信息带来Alex L.'s helpful answerakhan's helpful answerBlckknght's helpful answer在一起:

  • Standard Unix signal SIGPIPE被送到写到pipe过程当没有过程从管道读取(再)。

    • 这不一定是一个错误条件;一些Unix实用程序,如head设计一旦收到足够的数据,就会停止从管道中过早读取数据。
  • 默认情况下 - 即如果写作过程中没有明确陷阱SIGPIPE - 写作过程很简单终止,它的退出代码设置为141,其计算公式为(通常信号终止信号)+ 13SIGPIPE的特定信号号码)。

  • 在设计上,然而,的Python 本身陷阱SIGPIPE将其转换为一个Python IOError实例与errnoerrno.EPIPE,使Python脚本可以赶上它,如果它选择这样做 - 看Alex L.'s answer为怎么做。

  • 如果一个Python脚本,巨蟒输出错误信息IOError: [Errno 32] Broken pipe终止,退出代码为1脚本 - 这是症状的OP锯。

  • 在许多情况下这是比有用更具破坏性,所以恢复到默认的行为是可取的

    • 使用signal模块允许了这一点,如在规定akhan's answer; signal.signal()作为第二个参数处理第一个参数和处理程序;特殊句柄值SIG_DFL表示系统的默认行为:

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

这是一个很好的解释。 – ltw

14

我觉得有必要指出的是,该方法使用

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其中罗伯特·柯林斯表明,这种解决办法,我变成了一个装饰你可以用你的主要功能(是的,这是一些疯狂的缩进):

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