我有一个非常简单的Python 3脚本:
f1 = open('a.txt', 'r')
print(f1.readlines())
f2 = open('b.txt', 'r')
print(f2.readlines())
f3 = open('c.txt', 'r')
print(f3.readlines())
f4 = open('d.txt', 'r')
print(f4.readlines())
f1.close()
f2.close()
f3.close()
f4.close()
但它总是说:
IOError: [Errno 32] Broken pipe
我在互联网上看到了解决这个问题的所有复杂方法,但我直接复制了这段代码,所以我认为代码有问题而不是Python的SIGPIPE。
我正在重定向输出,所以如果上面的脚本命名为“open.py”,那么我的命令就是:
open.py | othercommand
答案 0 :(得分:108)
问题是由于SIGPIPE处理。您可以使用以下代码解决此问题:
from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE,SIG_DFL)
答案 1 :(得分:71)
将Alex L.'s helpful answer,akhan's helpful answer和Blckknght's helpful answer与其他信息结合在一起:
Standard Unix signal SIGPIPE
被发送到写入进程pipe了)。强>
默认情况下 - 即如果写入过程没有明确地陷阱 head
- 写作过程只是已终止,并且其退出代码设置为SIGPIPE
,其计算结果为141
(表示通常信号终止)+ 128
({{ 1}}的特定信号数字)。
但是,根据设计, Python 本身陷阱13
和将其转换为Python SIGPIPE
SIGPIPE
值为IOError
的实例,以便Python脚本能够捕获它,如果它选择的话 - 请参阅Alex L.'s answer了解如何执行此操作。
如果Python 脚本 不抓住它,Python 会输出错误消息errno
和使用退出代码errno.EPIPE
终止脚本 - 这是OP看到的症状。
在许多情况下这更具破坏性而非有用,因此需要恢复默认行为:
使用 IOError: [Errno 32] Broken pipe
模块就可以实现这一点,如akhan's answer中所述; signal.signal()
接收信号作为第一个参数处理,处理程序作为第二个参数处理;特殊处理程序值1
表示系统的默认行为:
signal
答案 2 :(得分:34)
我没有重现这个问题,但也许这个方法可以解决它:(逐行写入stdout
而不是print
)
import sys
with open('a.txt', 'r') as f1:
for line in f1:
sys.stdout.write(line)
你可以抓住破裂的烟斗吗?这会逐行将文件写入stdout
,直到管道关闭。
import sys, errno
try:
with open('a.txt', 'r') as f1:
for line in f1:
sys.stdout.write(line)
except IOError as e:
if e.errno == errno.EPIPE:
# Handle error
您还需要确保othercommand
在管道太大之前正在读取管道https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer
答案 3 :(得分:26)
当您尝试写入另一端已关闭的管道时,会出现“Broken Pipe”错误。由于您显示的代码不直接涉及任何管道,我怀疑您正在使用Python之外的东西将Python解释器的标准输出重定向到其他地方。如果你正在运行这样的脚本,可能会发生这种情况:
python foo.py | someothercommand
您遇到的问题是someothercommand
正在退出而未阅读其标准输入中的所有可用内容。这会导致您的写入(通过print
)在某些时候失败。
我能够在Linux系统上使用以下命令重现错误:
python -c 'for i in range(1000): print i' | less
如果我关闭less
寻呼机而没有滚动浏览所有输入(1000行),Python会以您报告的IOError
退出。
答案 4 :(得分:18)
我觉得有必要指出使用
的方法signal(SIGPIPE, SIG_DFL)
确实是危险(正如David Bennet在评论中已经提到的那样),在我的案例中,当与multiprocessing.Manager
结合时,导致了与平台相关的有趣业务(因为标准库依赖于在几个地方引发BrokenPipeError)。为了简短而痛苦的故事,这就是我修复它的方式:
首先,您需要捕获IOError
(Python 2)或BrokenPipeError
(Python 3)。根据您的程序,您可以尝试在此时提前退出,或者忽略例外:
from errno import EPIPE
try:
broken_pipe_exception = BrokenPipeError
except NameError: # Python 2
broken_pipe_exception = IOError
try:
YOUR CODE GOES HERE
except broken_pipe_exception as exc:
if broken_pipe_exception == IOError:
if exc.errno != EPIPE:
raise
然而,这还不够。 Python 3仍然可以打印这样的消息:
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe
不幸的是,摆脱那条消息并不简单,但我终于找到了http://bugs.python.org/issue11380罗伯特·柯林斯建议这种解决方法,我变成了一个装饰者,你可以用你的主要功能包装(是的,那是一些疯狂的缩进):
from functools import wraps
from sys import exit, stderr, stdout
from traceback import print_exc
def suppress_broken_pipe_msg(f):
@wraps(f)
def wrapper(*args, **kwargs):
try:
return f(*args, **kwargs)
except SystemExit:
raise
except:
print_exc()
exit(1)
finally:
try:
stdout.flush()
finally:
try:
stdout.close()
finally:
try:
stderr.flush()
finally:
stderr.close()
return wrapper
@suppress_broken_pipe_msg
def main():
YOUR CODE GOES HERE
答案 5 :(得分:2)
此处的最高答案(if e.errno == errno.EPIPE:
)对我而言并不真正有用。我得到了:
AttributeError: 'BrokenPipeError' object has no attribute 'EPIPE'
但是,如果您只关心忽略特定写入操作中的损坏管道,则此方法应该起作用。我认为这比捕获SIGPIPE更安全:
try:
# writing, flushing, whatever goes here
except BrokenPipeError:
exit( 0 )
很明显,您必须决定是否遇到断线的情况,代码是否真的完成了,但是对于大多数目的,我认为这通常是正确的。 (不要忘记关闭文件句柄等)
答案 6 :(得分:1)
如果脚本输出的读取结束过早死亡,也会发生这种情况
即open.py | otherCommand
如果otherCommand退出并且open.py尝试写入stdout
我有一个糟糕的gawk脚本,这对我来说很可爱。
答案 7 :(得分:1)
我知道这不是“正确”的方法,但是如果您只是想摆脱错误消息,可以尝试以下解决方法:
python your_python_code.py 2> /dev/null | other_command
答案 8 :(得分:0)
关闭应该以打开的相反顺序完成。
答案 9 :(得分:0)
根据问题的确切原因,设置环境变量 PYTHONUNBUFFERED=1
可能会有所帮助,这会强制不缓冲 stdout 和 stderr 流。请参阅:https://docs.python.org/3/using/cmdline.html#cmdoption-u
所以,你的命令
open.py | othercommand
变成:
PYTHONUNBUFFERED=1 open.py | othercommand
示例:
$ python3 -m http.server | tee -a access.log
^CException ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe
$ PYTHONUNBUFFERED=1 python3 -m http.server | tee -a access.log
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
^C
$