回答
import sys
for i in range(4000):
try:
print(i, flush=True)
except BrokenPipeError:
sys.stdout = None
解释
即使您捕获了BrokenPipeError异常,当您的程序退出并且 Python 尝试刷新标准输出时,Python 也会再次抛出该异常。通过将 stdout 设置为 None,Python 将不会尝试刷新它。
缺点
虽然 Python 例程(例如print()
)正确检查 stdout 是否为 None 并且不会失败,但看到不检查的程序并不少见。如果您的程序在将 stdout 设置为 None 后尝试使用stdout.write()
或类似的,那么 Python 将抛出一个 AttributeError。
其他答案(为什么不)
没有比 更短或更简单sys.stdout = None
的答案,但一些常见答案存在重大问题。
/dev/null
Python 开发人员有自己的建议代码来处理 BrokenPipeError。
import os
import sys
def main():
try:
# simulate large output (your code replaces this loop)
for x in range(10000):
print("y")
# flush output here to force SIGPIPE to be triggered
# while inside this try block.
sys.stdout.flush()
except BrokenPipeError:
# 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())
sys.exit(1) # Python exits with error code 1 on EPIPE
if __name__ == '__main__':
main()
虽然这是规范的答案,但它是相当奇怪的,因为它不必要地打开一个新的文件描述符到 /dev/null,以便 Python 可以在它关闭之前刷新它。
为什么不:对于大多数人来说,这是毫无意义的。这个问题是由 Python 刷新我们已经捕获到 BrokenPipeError 的句柄引起的。我们知道它会失败,所以解决方案应该是让 Python 简单地不刷新该句柄。分配一个新的文件描述符只是为了安抚 Python 是愚蠢的。
为什么(也许):将标准输出重定向到 /dev/null 实际上可能是某些人的正确解决方案,他们的程序在收到 BrokenPipeError 后将继续操作标准输出而不先检查它。但是,这不是常见的情况。
sys.stderr.close()
有些人建议关闭 stderr 以隐藏虚假的 BrokenPipe 错误消息。
为什么不:它还可以防止显示任何合法错误。
signal(SIGPIPE, SIG_DFL)
另一个常见的答案是使用SIG_DFL
默认信号处理程序 ,以在收到 SIGPIPE 信号时使程序终止。
为什么不呢:可以为任何文件描述符发送 SIGPIPE,而不仅仅是 stdout,因此如果您的整个程序正在写入连接被中断的网络套接字,那么您的整个程序就会突然神秘地死掉。
pipe.py | something | head
一个非 Python 解决方案是首先将标准输出通过管道传输到一个程序,该程序将继续从 Python 程序读取数据,即使它自己的标准输出已关闭。例如,假设您拥有 GNU 版本tee
,则可以:
pipe.py | tee -p /dev/null | head
为什么不:问题是在 Python 中寻找答案。此外,它会使 pipe.py 运行的时间超过它需要的时间,这可能会消耗大量资源,这也是次优的。