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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14207708/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 10:45:44  来源:igfitidea点击:

IOError: [Errno 32] Broken pipe: Python

pythonpython-3.xsigpipe

提问by JOHANNES_NY?TT

I have a very simple Python 3 script:

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

But it always says:

但它总是说:

IOError: [Errno 32] Broken pipe

I saw on the internet all the complicated ways to fix this, but I copied this code directly, so I think that there is something wrong with the code and not Python's SIGPIPE.

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

I am redirecting the output, so if the above script was named "open.py", then my command to run would be:

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

open.py | othercommand

采纳答案by Alex L

I haven't reproduced the issue, but perhaps this method would solve it: (writing line by line to stdoutrather than using print)

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

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


You could catch the broken pipe? This writes the file to stdoutline by line until the pipe is closed.

你能抓住断了的管子吗?这将文件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

You also need to make sure that othercommandis reading from the pipe before it gets too big - https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer

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

回答by Blckknght

A "Broken Pipe" error occurs when you try to write to a pipe that has been closed on the other end. Since the code you've shown doesn't involve any pipes directly, I suspect you're doing something outside of Python to redirect the standard output of the Python interpreter to somewhere else. This could happen if you're running a script like this:

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

python foo.py | someothercommand

The issue you have is that someothercommandis exiting without reading everything available on its standard input. This causes your write (via print) to fail at some point.

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

I was able to reproduce the error with the following command on a Linux system:

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

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

If I close the lesspager without scrolling through all of its input (1000 lines), Python exits with the same IOErroryou have reported.

如果我在less没有滚动所有输入(1000 行)的情况下关闭寻呼机,Python 会以IOError您报告的相同内容退出。

回答by akhan

The problem is due to SIGPIPE handling. You can solve this problem using the following code:

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

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

See herefor background on this solution. Better answer here.

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

回答by lkreinitz

This can also occur if the read end of the output from your script dies prematurely

如果脚本输出的读取端过早终止,也会发生这种情况

ie open.py | otherCommand

即 open.py | 其他命令

if otherCommand exits and open.py tries to write to stdout

如果 otherCommand 退出并且 open.py 尝试写入标准输出

I had a bad gawk script that did this lovely to me.

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

回答by Paul

Closes should be done in reverse order of the opens.

关闭应该以与打开相反的顺序进行。

回答by mklement0

To bring Alex L.'s helpful answer, akhan's helpful answer, and Blckknght's helpful answertogether with some additional information:

Alex L. 的有用答案akhan 的有用答案Blckknght 的有用答案以及一些附加信息带来:

  • Standard Unix signal SIGPIPEis sent to a process writingto a pipewhen there's no process readingfrom the pipe (anymore).

    • This is not necessarily an errorcondition; some Unix utilities such as headby designstop reading prematurely from a pipe, once they've received enough data.
  • By default- i.e., if the writing process does not explicitly trapSIGPIPE- the writing process is simply terminated, and its exit code is set to 141, which is calculated as 128(to signal termination by signal in general) + 13(SIGPIPE's specific signal number).

  • By design, however, Python itselftraps SIGPIPEand translates it into a Python IOErrorinstance with errnovalue errno.EPIPE, so that a Python script can catch it, if it so chooses - see Alex L.'s answerfor how to do that.

  • If a Python scriptdoes notcatch it, Python outputs error message IOError: [Errno 32] Broken pipeand terminates the script with exit code 1- this is the symptom the OP saw.

  • In many casesthis is more disruptive than helpful, so reverting to the default behavior is desirable:

    • Using the signalmoduleallows just that, as stated in akhan's answer; signal.signal()takes a signal to handle as the 1st argument and a handler as the 2nd; special handler value SIG_DFLrepresents the system's defaultbehavior:

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

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

  • 然而,按照设计,Python本身会捕获SIGPIPEIOError并将其转换为具有errnovalue的 Python实例errno.EPIPE,以便 Python 脚本可以捕获它,如果它选择的话 - 请参阅Alex L. 的答案以了解如何做到这一点。

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

  • 在许多情况下,比帮助更具破坏性,因此恢复到默认行为是可取的

    • 使用该signal模块可以做到这一点,正如akhan 的回答中所述signal.signal()将要处理的信号作为第一个参数,将处理程序作为第二个参数;特殊处理程序值SIG_DFL代表系统的默认行为:

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

回答by trehn

I feel obliged to point out that the method using

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

signal(SIGPIPE, SIG_DFL) 

is indeed dangerous(as already suggested by David Bennet in the comments) and in my case led to platform-dependent funny business when combined with multiprocessing.Manager(because the standard library relies on BrokenPipeError being raised in several places). To make a long and painful story short, this is how I fixed it:

确实很危险(正如 David Bennet 在评论中已经建议的那样),并且在我的情况下,当与它结合时会导致依赖于平台的有趣业务multiprocessing.Manager(因为标准库依赖于在多个地方引发的 BrokenPipeError)。为了使一个漫长而痛苦的故事简短,我是这样解决的:

First, you need to catch the IOError(Python 2) or BrokenPipeError(Python 3). Depending on your program you can try to exit early at that point or just ignore the exception:

首先,您需要捕获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

However, this isn't enough. Python 3 may still print a message like this:

然而,这还不够。Python 3 可能仍会打印如下消息:

Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe

Unfortunately getting rid of that message is not straightforward, but I finally found http://bugs.python.org/issue11380where Robert Collins suggests this workaround that I turned into a decorator you can wrap your main function with (yes, that's some crazy indentation):

不幸的是,摆脱该消息并不简单,但我终于找到了http://bugs.python.org/issue11380,其中 Robert Collins 提出了这个解决方法,我把它变成了一个装饰器,你可以用它来包装你的主函数(是的,这有点疯狂缩进):

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

回答by ssanch

I know this is not the "proper" way to do it, but if you are simply interested in getting rid of the error message, you could try this workaround:

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

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

回答by JD Baldwin

The top answer (if e.errno == errno.EPIPE:) here didn't really work for me. I got:

这里的最佳答案 ( if e.errno == errno.EPIPE:) 对我不起作用。我有:

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

However, this ought to work if all you care about is ignoring broken pipes on specific writes. I think it's safer than trapping SIGPIPE:

但是,如果您只关心忽略特定写入时损坏的管道,这应该可以工作。我认为这比捕获 SIGPIPE 更安全:

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

You obviously have to make a decision as to whether your code is really, truly done if you hit the broken pipe, but for most purposes I think that's usually going to be true. (Don't forget to close file handles, etc.)

显然,如果您遇到了损坏的管道,您显然必须决定您的代码是否真的,真的完成了,但对于大多数目的,我认为这通常是正确的。(不要忘记关闭文件句柄等)