管道传输时在python的子流程模块中使用stdout.close()

python子进程模块中,如果我们想运行shell命令

foo | grep bar

从python内部,我们可能会使用

p1 = Popen(["foo"], stdout = PIPE)
p2 = Popen(["grep", "bar"], stdin = p1.stdout, stdout = PIPE)
p1.stdout.close()
output = p2.communicate()[0]

我对p1.stdout.close()行感到困惑.如果您能原谅我,我将追踪我认为程序的工作方式,并且该错误将有望揭晓.

在我看来,当python颁布了output = p2.communicate()[0]时,python尝试调用p2,它识别出它需要从p1输出.因此,它调用p1,它执行foo并将输出扔到堆栈上,以便p2可以完成执行.然后p2完成.

但是在此跟踪中,实际上没有发生p1.stdout.close().那么实际发生了什么?在我看来,行的排序可能也很重要,因此以下内容将不起作用:

p1 = Popen(["foo"], stdout = PIPE)
p1.stdout.close()
p2 = Popen(["grep", "bar"], stdin = p1.stdout, stdout = PIPE)
output = p2.communicate()[0]

这就是我的理解状态.

解决方法:

p1.stdout.close()是foo检测管道何时断开(例如,p2过早退出)的必要条件.

如果没有p1.stdout.close(),则即使父级p2退出,p1.stdout仍保持打开状态. p1不会知道没有人读取p1.stdout,即p1将继续写入p1.stdout,直到相应的OS管道缓冲区已满,然后它将永远阻塞.

模拟foo |不带壳的grep bar shell命令:

#!/usr/bin/env python3
from subprocess import Popen, PIPE

with Popen(['grep', 'bar'], stdin=PIPE) as grep, \
     Popen(['foo'], stdout=grep.stdin):
    grep.communicate()

查看How do I use subprocess.Popen to connect multiple processes by pipes?

上一篇:python-未缓冲的子流程输出(最后一行缺失)


下一篇:我在Bash中有一系列命令时如何重定向stdin / stdout?