103

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

10 回答 10

123

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

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

更新:正如评论中指出的那样,python 文档已经有了一个很好的答案。

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

于 2013-05-31T20:05:58.950 回答
108

将来自许多有用答案的信息与一些附加信息结合在一起:

  • SIGPIPE当没有进程从管道读取时(不再),标准 Unix 信号被发送到写入管道的进程。

    • 这不一定是错误情况;某些 Unix 实用程序(例如head 设计使然)一旦接收到足够的数据,就会过早地停止从管道中读取。
    • 因此,引发此错误的一种简单方法是通过管道传输到head[1];例如:
      • python -c 'for x in range(10000): print(x)' | head -n 1
  • 默认情况下- 即如果写入过程没有显式陷阱 SIGPIPE141-128写入过程简单终止,其退出代码设置为.13SIGPIPE

  • 但是,根据设计,Python本身会捕获SIGPIPE并将其转换为BrokenPipeErrorIOErrorerrno具有value的 Python (Python 3) / (Python 2)实例errno.EPIPE

    • 注意:如果您在 Windows 上使用 Unix 仿真环境,错误可能会有所不同 - 请参阅此答案。
  • 如果 Python脚本没有捕获异常,Python输出错误消息BrokenPipeError: [Errno 32] Broken pipePython 3,可能两次Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>夹在中间)/ IOError: [Errno 32] Broken pipePython 2)并使用退出代码[2]终止脚本1- 这是 Johannes 的症状(OP ) 锯。

Windows注意事项(SIGPIPE仅 Unix 信号)

  • 如果您的脚本也需要直接在 Windows 上运行,您可能必须有条件地绕过引用的代码SIGPIPE,如本答案所示。

  • 如果您的脚本在 Windows 上的Unix 子系统SIGPIPE中运行,则信号的显示方式可能与 Unix 上不同- 请参阅此答案


有两种方法可以解决这个问题:

通常,建议将此异常静音,因为它可能表示严重的错误情况,具体取决于脚本的用途,例如网络套接字的接收端意外关闭。

  • 但是,如果您的脚本是一个命令行实用程序,那么安静终止可能不仅可以接受而且是首选,以便与标准head实用程序很好地配合,例如,您可以按照以下方式安静地中止signal.signal(),用于安装平台的默认信号处理程序(其行为如上所述),也如akhan 的回答所示(适用于 Python 3 和 2):
# ONLY SUITABLE FOR COMMAND-LINE UTILITIES

# Install the default signal handler.
from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE, SIG_DFL)

# Start printing many lines.
# If this gets interrupted with SIGPIPE, 
# the script aborts quietly, and the process exit code is set to
# 141 (128 + SIGPIPE)
for x in range(10000): print(x)
  • 否则,如果您想自己处理 SIGPIPE 触发的异常(适用于 Python 3 和 2,改编自docs):
import sys, os, errno

try:

  # Start printing many lines.
  for x in range(10000): print(x)

  # IMPORTANT: Flush stdout here, to ensure that the 
  # SIGPIPE-triggered exception can be caught.
  sys.stdout.flush()

except IOError as e: 
  # Note: Python 3 has the more specific BrokenPipeError,
  #       but this way the code works in Python 2 too.
  if e.errno != errno.EPIPE: raise e # Unrelated error, re-throw.

  # Python flushes standard streams on exit; redirect remaining output
  # to devnull to avoid another BrokenPipeError at shutdown
  devnull = os.open(os.devnull, os.O_WRONLY)
  os.dup2(devnull, sys.stdout.fileno())

  # ... perform other handling.
  # Note: You can't write to stdout here.
  #       (print() and sys.stdout.write won't work)
  #       However, sys.stderr.write() can be used.
  sys.stderr.write("SIGPIPE received, terminating.\n")

  # Finally, exit with an exit code of choice.
  sys.exit(141)

[1] 请注意,bash默认情况下,您只会看到head' 退出代码 - 这是0- 反映在$?之后。用于echo ${PIPESTATUS[0]}查看 Python 的退出代码。

[2] 奇怪的是,在 macOS 10.15.7 (Catalina) 和 Python 3.9.2(但不是 2.x)上,我看到了 exit code 120,但文档说1,这也是我在 Linux 上看到的。

于 2015-05-07T03:50:10.460 回答
51

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

于 2013-01-08T04:33:42.813 回答
31

当您尝试写入另一端已关闭的管道时,会发生“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您报告的相同方式退出。

于 2013-01-08T11:10:14.187 回答
22

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

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
于 2016-03-03T00:59:34.543 回答
4

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

python your_python_code.py 2> /dev/null | other_command
于 2018-12-06T20:52:12.003 回答
2

这里的最佳答案 ( if e.errno == errno.EPIPE:) 对我来说并没有真正起作用。我有:

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

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

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

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

于 2020-04-08T14:54:59.817 回答
1

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

即 open.py | 其他命令

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

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

于 2013-10-17T18:23:24.107 回答
1

根据问题的确切原因,设置一个环境变量可能会有所帮助,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
$ 
于 2021-07-08T01:16:09.443 回答
-2

关闭应该按照打开的相反顺序进行。

于 2015-01-28T09:56:05.397 回答