在 Python 中执行刷新时如何防止 BrokenPipeError?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26692284/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to prevent BrokenPipeError when doing a flush in Python?
提问by tommy.carstensen
Question: Is there a way to use flush=Truefor the print()function without getting the BrokenPipeError?
问题:有没有办法flush=True在print()不获取 的情况下使用该函数BrokenPipeError?
I have a script pipe.py:
我有一个脚本pipe.py:
for i in range(4000):
print(i)
I call it like this from a Unix command line:
我在 Unix 命令行中这样称呼它:
python3 pipe.py | head -n3000
And it returns:
它返回:
0
1
2
So does this script:
这个脚本也是如此:
import sys
for i in range(4000):
print(i)
sys.stdout.flush()
However, when I run this script and pipe it to head -n3000:
但是,当我运行此脚本并将其通过管道传输到head -n3000:
for i in range(4000):
print(i, flush=True)
Then I get this error:
然后我收到这个错误:
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
I have also tried the solution below, but I still get the BrokenPipeError:
我也尝试了下面的解决方案,但我仍然得到BrokenPipeError:
import sys
for i in range(4000):
try:
print(i, flush=True)
except BrokenPipeError:
sys.exit()
采纳答案by Serge Ballesta
The BrokenPipeErroris normal as said phantom because the reading process (head) terminates and closes its end of the pipe while the writing process (python) still tries to write.
这BrokenPipeError是正常的,因为读取进程(头)终止并关闭管道的末端,而写入进程(python)仍在尝试写入。
Is isan abnormal condition, and the python scripts receives a BrokenPipeError- more exactly, the Python interpreter receives a system SIGPIPE signal that it catches and raises the BrokenPipeErrorto allow the script to process the error.
这是一个异常情况,python 脚本收到一个BrokenPipeError- 更准确地说,Python 解释器收到一个系统 SIGPIPE 信号,它捕获并引发BrokenPipeError允许脚本处理错误。
And you effectively can process the error, because in your last example, you only see a message saying that the exception was ignored - ok it is not true, but seems related to this open issuein Python : Python developpers think important to warn user of the abnormal condition.
并且您可以有效地处理错误,因为在您的上一个示例中,您只会看到一条消息,指出异常已被忽略 - 好吧,这不是真的,但似乎与Python 中的这个未解决的问题有关:Python 开发人员认为重要的是要警告用户异常情况。
What really happens is that AFAIK the python interpreter always signals this on stderr, even if you catch the exception. But you just have to close stderr before exiting to get rid of the message.
真正发生的是 AFAIK,python 解释器总是在 stderr 上发出信号,即使您捕获了异常。但是您只需要在退出之前关闭 stderr 即可消除该消息。
I slightly changed your script to :
我稍微将您的脚本更改为:
- catch the error as you did in your last example
- catch either IOError (that I get in Python34 on Windows64) or BrokenPipeError (in Python 33 on FreeBSD 9.0) - and display a message for that
- display a custom Donemessage on stderr (stdout is closed due to the broken pipe)
- close stderrbefore exiting to get rid of the message
- 像在上一个示例中一样捕获错误
- 捕获 IOError(我在 Windows64 上的 Python34 中得到)或 BrokenPipeError(在 FreeBSD 9.0 上的 Python 33 中) - 并显示一条消息
- 在 stderr 上显示自定义的Done消息(由于管道损坏,stdout 已关闭)
- 退出前关闭 stderr以消除消息
Here is the script I used :
这是我使用的脚本:
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()
and here the result of python3.3 pipe.py | head -10:
和这里的结果python3.3 pipe.py | head -10:
0
1
2
3
4
5
6
7
8
9
BrokenPipeError caught
Done
If you do not want the extraneous messages just use :
如果您不想要无关的消息,请使用:
import sys
try:
for i in range(4000):
print(i, flush=True)
except (BrokenPipeError, IOError):
pass
sys.stderr.close()
回答by phantom
According to the Python documentation, this is thrown when:
根据 Python 文档,在以下情况下会抛出此问题:
trying to write on a pipe while the other end has been closed
试图在另一端关闭的情况下在管道上写字
This is due to the fact that the head utility reads from stdout, then promptly closes it.
这是因为 head 实用程序读取数据stdout,然后立即关闭它。
As you can see, it can be worked around by merely adding a sys.stdout.flush()after every print(). Note that this sometimes does not work in Python 3.
如您所见,只需sys.stdout.flush()在每个print(). 请注意,这有时在 Python 3 中不起作用。
You can alternatively pipe it to awklike this to get the same result as head -3:
您也可以通过管道将其设置为awk这样,以获得与以下相同的结果head -3:
python3 0to3.py | awk 'NR >= 4 {exit} 1'
Hope this helped, good luck!
希望这有帮助,祝你好运!
回答by Michele d'Amico
As you can see in the output that you had posted the last exception is raised in the destructor phase : that is why you have ignoredat the end
正如您在输出中看到的,您发布的最后一个异常是在析构函数阶段引发的:这就是为什么您ignored在最后有
Exception BrokenPipeError: BrokenPipeError(32, 'Broken pipe') in <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'> ignored
A simple example to understand what's up in that context is the follow:
一个简单的例子来理解在这种情况下发生了什么:
>> 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
Every exception that is triggered while the object is destroyed will cause a standard error output that explain the exception occurred and ignored (that is because python will inform you that something could not be correctly handle in destroy phase). Anyway, that kind of exceptions cannot be cached and so you can just remove the calls that can generate it or close stderr.
销毁对象时触发的每个异常都会导致标准错误输出,解释异常发生并被忽略(这是因为 python 会通知您在销毁阶段无法正确处理某些事情)。无论如何,这种异常无法缓存,因此您可以删除可以生成它的调用或 close stderr。
Come back to the question. That exception is not a real problem (as say it is ignored) but if you don't want print it you must override the the function that can be called when the object will be destroyed or close stderras @SergeBallesta correctly suggested : in you case you can shutdownwriteand flushfunction and no exception will be triggered in destroy context
回到问题上来。该异常不是真正的问题(如说它被忽略),但如果您不想打印它,则必须覆盖在对象将被销毁或关闭时可以调用的函数,stderr正如@SergeBallesta 正确建议的那样:在您的情况下您可以关闭write和flush运行,并且在销毁上下文中不会触发任何异常
That is an example of how you can do it:
这是一个如何做到这一点的例子:
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()
回答by notpeter
While others have covered the underlying issue in great detail there is a straightforward workaround:
虽然其他人已经非常详细地介绍了潜在问题,但有一个简单的解决方法:
python whatever.py | tail -n +1 | head -n3000
Explanation: tailbuffers until it's STDIN is closed (python quits and closes its STDOUT). So only tail gets the SIGPIPE when head quits. The -n +1is effectively a no-op, making tail output the "tail" starting at line 1, which is the entire buffer.
说明:tail缓冲直到 STDIN 关闭(python 退出并关闭其 STDOUT)。所以当 head 退出时,只有 tail 获得 SIGPIPE。该-n +1有效是一个空操作,使得尾输出的“尾巴”起始于线1,其是整个缓冲区。
回答by Waxrat
I've often wished there were a command-line option to suppress these signal handlers.
我经常希望有一个命令行选项来抑制这些信号处理程序。
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)
Instead, the best we can do is uninstall the handlers as soon as possible as the Python script starts running.
相反,我们能做的最好的事情是在 Python 脚本开始运行时尽快卸载处理程序。
回答by skovorodkin
A note on SIGPIPEwas addedin Python 3.7 documentation, and it recommends to catch BrokenPipeErrorthis way:
一个音符在SIGPIPE被添加的Python 3.7文档,并建议赶上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()
Importantly, it says:
重要的是,它说:
Do not set
SIGPIPE's disposition toSIG_DFLin order to avoidBrokenPipeError. Doing that would cause your program to exit unexpectedly also whenever any socket connection is interrupted while your program is still writing to it.
请勿设置为避免设置
SIGPIPE的配置。这样做会导致您的程序意外退出,只要您的程序仍在写入时任何套接字连接被中断。SIG_DFLBrokenPipeError

