问题:有没有办法flush=True
print()
功能使用pipe.py
而无法获取BrokenPipeError
?
我有一个脚本for i in range(4000):
print(i)
:
python3 pipe.py | head -n3000
我从Unix命令行这样称呼它:
0
1
2
它返回:
import sys
for i in range(4000):
print(i)
sys.stdout.flush()
这个脚本也是如此:
head -n3000
但是,当我运行此脚本并将其传递给for i in range(4000):
print(i, flush=True)
:
print(i, flush=True)
BrokenPipeError: [Errno 32] Broken pipe
Exception BrokenPipeError: BrokenPipeError(32, 'Broken pipe') in <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> ignored
然后我收到此错误:
BrokenPipeError
我也尝试了下面的解决方案,但我仍然得到了import sys
for i in range(4000):
try:
print(i, flush=True)
except BrokenPipeError:
sys.exit()
:
{{1}}
答案 0 :(得分:22)
BrokenPipeError
是正常的,因为读取过程(head)终止并关闭管道的末尾,而写入过程(python)仍然试图写入。
是一个异常情况,python脚本收到BrokenPipeError
- 更准确地说,Python解释器收到一个系统SIGPIPE信号,它捕获并引发BrokenPipeError
允许脚本处理错误。
并且您可以有效地处理错误,因为在上一个示例中,您只看到一条消息说该异常被忽略 - 确定它不是真的,但似乎与Python中的open issue相关:Python开发人员认为重要的是警告用户异常情况。
真正发生的是AFAIK python解释器总是在stderr上发出这个信号,即使你发现异常。但是你必须在退出之前关闭stderr以消除信息。
我稍稍将您的脚本更改为:
这是我使用的脚本:
import sys
try:
for i in range(4000):
print(i, flush=True)
except (BrokenPipeError, IOError):
print ('BrokenPipeError caught', file = sys.stderr)
print ('Done', file=sys.stderr)
sys.stderr.close()
这里是python3.3 pipe.py | head -10
的结果:
0
1
2
3
4
5
6
7
8
9
BrokenPipeError caught
Done
如果您不想使用无关的消息:
import sys
try:
for i in range(4000):
print(i, flush=True)
except (BrokenPipeError, IOError):
pass
sys.stderr.close()
答案 1 :(得分:4)
根据Python文档,在以下情况下抛出此内容:
尝试在管道上写入而另一端已经关闭
这是因为head实用程序从stdout
读取,然后立即关闭它。
正如您所看到的,只需在每sys.stdout.flush()
之后添加print()
即可解决此问题。请注意,这有时在Python 3中不起作用。
您可以选择将其传递给awk
,以获得与head -3
相同的结果:
python3 0to3.py | awk 'NR >= 4 {exit} 1'
希望这有所帮助,祝你好运!
答案 2 :(得分:2)
在Python 3.7文档中,note on SIGPIPE
是added,它建议以这种方式捕获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()
重要的是,它说:
请勿将
SIGPIPE
的性格设置为SIG_DFL
,以避免BrokenPipeError
。这样做还会导致在程序仍在对其进行写操作时任何套接字连接中断时,程序也会异常退出。
答案 3 :(得分:1)
正如您在输出中看到的那样,在析构函数阶段引发了最后一个异常:这就是为什么你最后有ignored
Exception BrokenPipeError: BrokenPipeError(32, 'Broken pipe') in <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> ignored
一个简单的例子来理解该上下文中的内容如下:
>> class A():
... def __del__(self):
... raise Exception("It will be ignored!!!")
...
>>> a = A()
>>> del a
Exception Exception: Exception('It will be ignored!!!',) in <bound method A.__del__ of <__builtin__.A instance at 0x7ff1d5c06d88>> ignored
>>> a = A()
>>> import sys
>>> sys.stderr.close()
>>> del a
在销毁对象时触发的每个异常都会导致标准错误输出,解释发生的异常并被忽略(因为python会通知您在破坏阶段无法正确处理某些内容)。无论如何,这种异常无法缓存,因此您可以删除可以生成它或关闭stderr
的调用。
回到这个问题。这个异常不是一个真正的问题(比如说它被忽略了)但是如果你不想打印它,你必须覆盖当对象被销毁时可以调用的函数或者关闭stderr
作为@ SergeBallesta正确地建议:在你的情况下你可以关闭 write
和flush
函数,并且在破坏上下文中不会触发异常
这是一个如何做到这一点的例子:
import sys
def _void_f(*args,**kwargs):
pass
for i in range(4000):
try:
print(i,flush=True)
except (BrokenPipeError, IOError):
sys.stdout.write = _void_f
sys.stdout.flush = _void_f
sys.exit()
答案 4 :(得分:0)
暂时忽略SIGPPIE
我不确定这个主意有多糟,但是它能起作用:
#!/usr/bin/env python3
import signal
import sys
sigpipe_old = signal.getsignal(signal.SIGPIPE)
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
for i in range(4000):
print(i, flush=True)
signal.signal(signal.SIGPIPE, sigpipe_old)
答案 5 :(得分:0)
尽管其他人已经详细介绍了潜在问题,但是有一个简单的解决方法:
python whatever.py | tail -n +1 | head -n3000
说明:tail
缓冲直到其STDIN关闭(python退出并关闭其STDOUT)。因此,当头部退出时,只有尾巴会获得SIGPIPE。 -n +1
实际上是一个空操作,使尾部输出从第1行开始即整个缓冲区成为尾部。
答案 6 :(得分:0)
我通常希望有一个命令行选项来禁止这些信号处理程序。
import signal
# Don't turn these signal into exceptions, just die.
signal.signal(signal.SIGINT, signal.SIG_DFL)
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
相反,我们能做的最好的事情就是在Python脚本开始运行后尽快卸载处理程序。