BrokenPipeError: [Errno 32] Broken pipe
当通过管道传输到类似命令时,以下脚本将引发head
(除非头部的行数超过了 Python 脚本打印的行数)。
for i in range(16386):
print("")
$ python test-pipe.py | head -1
Traceback (most recent call last):
File "test-pipe.py", line 2, in <module>
print("")
BrokenPipeError: [Errno 32] Broken pipe
我的理解(来自这个答案和这个问题的答案)的问题是,如果在 Python 进程完成写入之前关闭管道,则会引发错误。
但是,如果我将迭代范围减少 1 到 16385,则不会引发错误(不确定此阈值在所有机器上是否相同,因此也许只需尝试一个高和低的数字即可重现)。我最初认为这可能与管道缓冲区大小有关,但对我来说是 64K(根据M=0; while printf A; do >&2 printf "\r$((++M)) B"; done | sleep 999
,所以这似乎不是原因。
为什么发生的情况BrokenPipeError
取决于管道的大小?
这是 Linux 5.4.15-arch1-1 上的 Python 3.8.1。
答案1
为什么 BrokenPipeError 的发生取决于管道的大小?
因为写更多的东西需要更多的时间,并且管道的右侧可能会在你的Python写完之前就死掉了。另外,如果 python 尝试写入的内容超出了管道缓冲区的容量,它会阻塞并有head -1
足够的时间退出。
由于head -1
生存和死亡需要一些时间,因此 python 可能会利用这段时间来写入所有内容(如果它适合管道缓冲区)并成功退出。这很难预测,因为内核可以自由地以任何顺序调度管道的两侧,并且可以在head -1
它认为合适的时候延迟启动,或者可以随时停止它。
>>> python3 -c 'for i in range(50000): print("")' | sleep .01
Traceback (most recent call last):
File "<string>", line 1, in <module>
BrokenPipeError: [Errno 32] Broken pipe
>>> python3 -c 'for i in range(50000): print("")' | sleep .1
# OK!
但是,如果 python 试图编写比管道容纳的更多的东西,那么无论它有多少时间,它最终都会不可避免地得到一个EPIPE
or :SIGPIPE
>>> python3 -c 'for i in range(100000): print("")' | sleep 20
Traceback (most recent call last):
File "<string>", line 1, in <module>
BrokenPipeError: [Errno 32] Broken pipe
但这对我来说是 64K...所以这似乎不是原因。
请记住 python 正在使用全缓冲当输出不是终端时;它不是逐行或逐字节地写入,而是按块写入一些尺寸:
>>> strace -s3 -e trace=write python3 -c 'for i in range(50000): print("")' | sleep .3
write(1, "\n\n\n"..., 8193) = 8193
write(1, "\n\n\n"..., 8193) = 8193
write(1, "\n\n\n"..., 8193) = 8193
write(1, "\n\n\n"..., 8193) = 8193
write(1, "\n\n\n"..., 8193) = 8193
write(1, "\n\n\n"..., 8193) = 8193
write(1, "\n\n\n"..., 842) = 842
+++ exited with 0 +++