IOError:[Errno 32] 管道损坏:`prog.py | othercmd`

2025-03-19 08:56:00
admin
原创
53
摘要:问题描述:我有一个非常简单的 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.rea...

问题描述:

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

解决方案 1:

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

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

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

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

解决方案 2:

将许多有用的答案中的信息汇总在一起,并提供一些附加信息:

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

+ 这不一定是*错误*情况;一些 Unix 实用程序(例如,`head` *按照设计)*一旦收到足够的数据就会过早地停止从管道读取。
+ 因此,引发此错误的一个简单方法是通过管道传输到`head`[1];例如:

    - `python -c 'for x in range(10000): print(x)' | head -n 1`
  • 默认情况下- 即,如果写入过程没有明确捕获 SIGPIPE- 写入过程将简单地终止,并且其退出代码设置为141,其计算方式为128(通常通过信号终止信号)+ 13SIGPIPE的特定信号编号)。

  • 然而,根据设计,Python本身会捕获SIGPIPE它并将其转换为具有值的Python BrokenPipeError(Python 3) / IOError(Python 2)实例。errno`errno.EPIPE`

+ *注意:如果您在 Windows 上*使用 Unix 仿真环境,错误可能会以不同的方式出现 - 请参阅此答案。
  • 如果 Python脚本没有捕获异常,Python就会输出错误消息BrokenPipeError: [Errno 32] Broken pipe( Python 3,可能两次Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>中间夹着)/ IOError: [Errno 32] Broken pipe( Python 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,改编自文档):

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.
")

  # 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)时,我看到退出代码120,但文档说1,这也是我在 Linux 上看到的。

解决方案 3:

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

解决方案 4:

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

python foo.py | someothercommand

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

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

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

如果我关闭less寻呼机而不滚动浏览其所有输入(1000 行),Python 将退出并显示IOError您报告的内容。

解决方案 5:

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

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,其中 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

解决方案 6:

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

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

解决方案 7:

这里的最佳答案 ( if e.errno == errno.EPIPE:) 对我来说并没有太大用处。我得到了:

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

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

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

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

解决方案 8:

根据问题的具体原因,设置环境变量可能会有所帮助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
$ 

解决方案 9:

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

即 open.py | otherCommand

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

我有一个糟糕的 gawk 脚本,它对我产生了这种影响。

解决方案 10:

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

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用