为了将有用的答案整合在一起,并添加一些额外的信息:
当一个进程从一个管道写入数据时,如果没有其他进程在读取该管道(或已经停止读取),则会向该进程发送标准Unix信号SIGPIPE
。
- 这不一定是一个错误的状态;一些Unix实用程序(如
head
)在接收到足够的数据后就有意地提前停止从管道中读取。
- 因此,触发此错误的简单方法是使用
head
命令进行管道传输(参见以下示例):
python -c 'for x in range(10000): print(x)' | head -n 1
默认情况下,即如果写入进程没有显式地捕获SIGPIPE
信号,则该进程将被简单地终止,并且其退出代码将被设置为141
,计算公式为128
(一般情况下表示通过信号终止) + 13
(代表SIGPIPE
信号)。
然而,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而言),并且以退出代码1
终止该脚本[2] - 这就是Johannes(原问题作者)所看到的症状。
Windows注意事项(SIGPIPE
是仅适用于Unix的信号)
解决此问题有两种方法:
通常,不建议 消除 此异常,因为它可能表示严重错误条件,取决于您的脚本目的,例如网络套接字接收端意外关闭。
- 但是,如果您的脚本是一个命令行实用程序,其中安静的终止不仅可以被接受,而且还可以优先考虑,以便与标准
head
实用程序很好地配合使用,您可以使用signal.signal()
来安装平台的默认信号处理程序(其行为如上所述),如akhan的回答所示(适用于Python 3和2):
from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE, SIG_DFL)
for x in range(10000): print(x)
- 否则,如果你想要自己处理由SIGPIPE触发的异常(适用于Python 3和2,改编自文档):
import sys, os, errno
try:
for x in range(10000): print(x)
sys.stdout.flush()
except IOError as e:
if e.errno != errno.EPIPE: raise e
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
sys.stderr.write("SIGPIPE received, terminating.\n")
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上看到的。
print(f1.readlines())
- JOHANNES_NYÅTTa.txt
读取和写入到stdout
。也许尝试将它们分成单独的行,以便您可以看到哪个操作触发了异常。如果stdout
是一个管道,并且读端已关闭,则可能会导致EPIPE
错误。 - James Henstridgeprint
调用是罪魁祸首。@JOHANNES_NYÅTT,你能澄清一下你是如何启动你的Python脚本的吗?你是否将标准输出重定向到某个地方? - Blckknght