python 子进程: "写入错误:管道中断

19 投票
5 回答
37184 浏览
提问于 2025-04-16 06:36

我遇到了一个问题,关于如何使用简单的subprocess.Popen来处理数据。

代码如下:

import subprocess
cmd = 'cat file | sort -g -k3 | head -20 | cut -f2,3' % (pattern,file)
p = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
for line in p.stdout:
    print(line.decode().strip())

对于大约1000行的文件,输出结果是:

...
sort: write failed: standard output: Broken pipe
sort: write error

对于超过241行的文件,输出结果是:

...
sort: fflush failed: standard output: Broken pipe
sort: write error

而对于少于241行的文件,输出结果是正常的。

我一直在阅读文档,也在网上疯狂搜索,但我觉得我对subprocess模块有一些基本的理解没有搞清楚……可能是和缓冲区有关。我试过使用p.stdout.flush(),还调整了缓冲区的大小和p.wait()。我也尝试用像'sleep 20; cat moderatefile'这样的命令来重现这个问题,但似乎没有出现错误。

5 个回答

1

我也遇到了同样的错误。我甚至把管道放进了一个bash脚本里执行,而不是直接在Python中使用管道。在Python中会出现断开的管道错误,但在bash中却没有。

我觉得可能是在head之前的最后一个命令出错了,因为它的输出(STDOUT)被关闭了。Python可能会捕捉到这个错误,而在shell中这个错误是静默的。我把代码改成了处理整个输入,这样错误就消失了。

对于较小的文件来说,这也能解释得通,因为管道可能会在head退出之前先把整个输出缓存起来。这就能解释为什么在处理较大文件时会出现问题。

比如,我原本是想用'head -1'(在我的情况下,我只想要第一行),但我改成了awk 'NR == 1'。

根据'head -X'在管道中的位置,可能还有更好的方法来实现这个功能。

5

这是因为在传给 subprocess.Popen 的命令中不应该使用“shell管道”,你应该使用 subprocess.PIPE,像这样:

from subprocess import Popen, PIPE

p1 = Popen('cat file', stdout=PIPE)
p2 = Popen('sort -g -k 3', stdin=p1.stdout, stdout=PIPE)
p3 = Popen('head -20', stdin=p2.stdout, stdout=PIPE)
p4 = Popen('cut -f2,3', stdin=p3.stdout)
final_output = p4.stdout.read()

不过我得说,你想做的事情其实可以用纯Python来完成,而不是调用一堆shell命令。

15

来自subprocess文档中的示例:

# To replace shell pipeline like output=`dmesg | grep hda`
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

撰写回答