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

JOH*_*ÅTT 82 python sigpipe python-3.x

我有一个非常简单的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()
Run Code Online (Sandbox Code Playgroud)

但它总是说:

IOError: [Errno 32] Broken pipe
Run Code Online (Sandbox Code Playgroud)

我在互联网上看到了解决这个问题的所有复杂方法,但是我直接复制了这个代码,所以我认为代码有问题而不是Python的SIGPIPE.

我正在重定向输出,所以如果上面的脚本命名为"open.py",那么我的运行命令是:

open.py | othercommand
Run Code Online (Sandbox Code Playgroud)

akh*_*han 111

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

from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE,SIG_DFL) 
Run Code Online (Sandbox Code Playgroud)

有关此解决方案的背景,请参见此处 这里更好的答案.

  • 这是非常危险的,正如我刚刚发现的那样,因为如果你在套接字(httplib或其他)上获得了一个SIGPIPE,你的程序将在没有警告或错误的情况下退出. (12认同)
  • @DavidBennett,我确信它取决于应用程序,并且出于您的目的,接受的答案是正确的。[此处](/sf/ask/7572841/) 有一个非常全面的问答,供人们浏览并做出明智的决定。IMO,对于命令行工具,在大多数情况下最好忽略管道信号。 (2认同)
  • @NateGlenn您可以保存[现有处理程序](https://docs.python.org/2/library/signal.html#signal.getsignal)并稍后恢复. (2认同)
  • 有人可以回答我为什么人们认为Blogspot文章是比[官方文档](https://docs.python.org/3/library/signal.html#note-on-sigpipe)更好的真理来源(提示:打开链接以查看如何正确修复折断的管道错误)?:) (2认同)

mkl*_*nt0 79

带来Alex L.的有用答案, akhan的有用答案,以及Blckknght的有用答案以及一些其他信息:

  • 标准Unix信号SIGPIPE被发送到进程写入到一个管道时,有没有进程读取从管道(了).

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

  • 然而,根据设计,Python 本身SIGPIPE会将IOError陷阱并将其转换为具有errno的Python实例errno.EPIPE,以便Python脚本可以捕获它,如果它选择的话 - 请参阅Alex L.的答案,了解如何执行此操作.

  • 如果一个Python 脚本没有抓住它,Python的输出错误信息IOError: [Errno 32] Broken pipe终止,退出代码脚本1 -这是症状的OP锯.

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


Ale*_*x L 40

我没有重现这个问题,但也许这个方法可以解决它:(逐行写stdout而不是使用print)

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

你能抓到破裂的管子吗?这会将文件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
Run Code Online (Sandbox Code Playgroud)

您还需要确保othercommand在管道太大之前从管道读取 - https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer

  • 虽然这是一个很好的编程实践,但我认为这与提问者得到的破坏管道错误无关(这可能与`print`调用有关,而不是与读取文件有关). (6认同)

Blc*_*ght 28

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

python foo.py | someothercommand
Run Code Online (Sandbox Code Playgroud)

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

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

python -c 'for i in range(1000): print i' | less
Run Code Online (Sandbox Code Playgroud)

如果我在less没有滚动浏览所有输入(1000行)的情况下关闭寻呼机,Python将以IOError您报告的相同方式退出.

  • 是的,这是真的,但我该如何解决? (7认同)
  • @Blckknght:一般情况良好,但重新"_fix_ that:和"正在做_wrong_事件的部分":`SIGPIPE`信号不一定表示_error_条件;一些Unix实用程序,特别是`head`,_by design,在正常操作期间_一旦他们读取了所需的_they_数据,就尽早关闭管道. (4认同)
  • 请让我知道如何解决它. (2认同)
  • 我在管道到头时遇到了这个问题......十行输出后的异常。很合乎逻辑,但仍然出乎意料:) (2认同)
  • “那是因为你的管道可能只有 64kB 左右 - 如果你尝试向它写入太多数据而其他进程没有读取它,那么它会抛出错误。” 参考该评论,我认为这不是真的 - 下一次写入将简单地阻塞,直到读者通过读取它来清空[一些]缓冲区。 (2认同)

tre*_*ehn 19

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

signal(SIGPIPE, SIG_DFL) 
Run Code Online (Sandbox Code Playgroud)

确实是危险的(正如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
Run Code Online (Sandbox Code Playgroud)

但是,这还不够.Python 3仍然可以打印这样的消息:

Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe
Run Code Online (Sandbox Code Playgroud)

不幸的是摆脱那条消息并不简单,但我终于找到了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
Run Code Online (Sandbox Code Playgroud)

  • 这似乎并没有为我解决。 (3认同)