IOError:[Errno 32]管道损坏:Python

时间:2013-01-08 03:18:28

标签: python python-3.x sigpipe

我有一个非常简单的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

10 个答案:

答案 0 :(得分:108)

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

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

See here了解此解决方案的背景知识。更好地回答here

答案 1 :(得分:71)

Alex L.'s helpful answerakhan's helpful answerBlckknght's helpful answer与其他信息结合在一起:

    当管道中没有进程读取时,
  • Standard Unix signal SIGPIPE被发送到写入进程pipe了)。

    • 这不一定是错误条件;某些Unix实用程序(例如{/ 1}} 按设计一旦收到足够的数据就会停止从管道中提前读取。
  • 默认情况下 - 即如果写入过程没有明确地陷阱 head - 写作过程只是已终止,并且其退出代码设置为SIGPIPE ,其计算结果为141(表示通常信号终止)+ 128({{ 1}}的特定信号数字)。

  • 但是,根据设计, Python 本身陷阱13 将其转换为Python SIGPIPE SIGPIPE值为IOError的实例,以便Python脚本能够捕获它,如果它选择的话 - 请参阅Alex L.'s answer了解如何执行此操作。

  • 如果Python 脚本 抓住它,Python 会输出错误消息errno 使用退出代码errno.EPIPE 终止脚本 - 这是OP看到的症状。

  • 在许多情况下更具破坏性而非有用,因此需要恢复默认行为

    • 使用 IOError: [Errno 32] Broken pipe模块就可以实现这一点,如akhan's answer中所述; signal.signal()接收信号作为第一个参数处理,处理程序作为第二个参数处理;特殊处理程序值1表示系统的默认行为:

      signal

答案 2 :(得分:34)

我没有重现这个问题,但也许这个方法可以解决它:(逐行写入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

答案 3 :(得分:26)

当您尝试写入另一端已关闭的管道时,会出现“Broken Pipe”错误。由于您显示的代码不直接涉及任何管道,我怀疑您正在使用Python之外的东西将Python解释器的标准输出重定向到其他地方。如果你正在运行这样的脚本,可能会发生这种情况:

python foo.py | someothercommand

您遇到的问题是someothercommand正在退出而未阅读其标准输入中的所有可用内容。这会导致您的写入(通过print)在某些时候失败。

我能够在Linux系统上使用以下命令重现错误:

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

如果我关闭less寻呼机而没有滚动浏览所有输入(1000行),Python会以您报告的IOError退出。

答案 4 :(得分:18)

我觉得有必要指出使用

的方法
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

答案 5 :(得分:2)

此处的最高答案(if e.errno == errno.EPIPE:)对我而言并不真正有用。我得到了:

AttributeError: 'BrokenPipeError' object has no attribute 'EPIPE'

但是,如果您只关心忽略特定写入操作中的损坏管道,则此方法应该起作用。我认为这比捕获SIGPIPE更安全:

try:
    # writing, flushing, whatever goes here
except BrokenPipeError:
    exit( 0 )

很明显,您必须决定是否遇到断线的情况,代码是否真的完成了,但是对于大多数目的,我认为这通常是正确的。 (不要忘记关闭文件句柄等)

答案 6 :(得分:1)

如果脚本输出的读取结束过早死亡,也会发生这种情况

即open.py | otherCommand

如果otherCommand退出并且open.py尝试写入stdout

我有一个糟糕的gawk脚本,这对我来说很可爱。

答案 7 :(得分:1)

我知道这不是“正确”的方法,但是如果您只是想摆脱错误消息,可以尝试以下解决方法:

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

答案 8 :(得分:0)

关闭应该以打开的相反顺序完成。

答案 9 :(得分:0)

根据问题的确切原因,设置环境变量 PYTHONUNBUFFERED=1 可能会有所帮助,这会强制不缓冲 stdout 和 stderr 流。请参阅:https://docs.python.org/3/using/cmdline.html#cmdoption-u

所以,你的命令

open.py | othercommand

变成:

PYTHONUNBUFFERED=1 open.py | othercommand

示例:

$ python3 -m http.server | tee -a access.log
^CException ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe

$ PYTHONUNBUFFERED=1 python3 -m http.server | tee -a access.log
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
^C
$