将来自许多有用答案的信息与一些附加信息结合在一起:
-
Standard Unix signal SIGPIPE 被发送到进程写入到pipe,当没有进程读取管道时(不再)。
- 这不一定是错误条件;一些 Unix 实用程序,例如
head,设计一旦收到足够的数据,就会过早地停止从管道读取。
- 因此,引发此错误的一种简单方法是通过管道传送到
head[1];例如。:
python -c 'for x in range(10000): print(x)' | head -n 1
-
默认情况下 - 即,如果写入过程没有明确陷阱 SIGPIPE - 写入过程只是终止 ,其退出码设置为141,计算为128(一般通过信号终止信号)+13(SIGPIPE的具体信号编号 em>)。
-
然而,根据设计 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(OP)看到的症状。
Windows 注意事项(SIGPIPE 是 Unix 专用信号)
有两种方法可以解决这个问题:
一般来说,不建议沉默此异常,因为它可能表示严重的错误情况,具体取决于脚本的用途,例如网络套接字的接收端意外关闭。
- 但是,如果您的脚本是命令行实用程序,安静终止不仅可以接受,而且首选以便播放与标准的
head 实用程序完美搭配,例如,您可以安静地如下使用signal.signal() 安装平台的默认信号处理程序(其行为如上所述),如 akhan's answer 中所示(适用于 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),我看到退出代码 120,但文档说 1,并且这也是我在 Linux 上看到的。