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

dav*_*uda 4 python subprocess pipe

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

foo | grep bar
Run Code Online (Sandbox Code Playgroud)

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

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

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

在我看来,当该行output = p2.communicate()[0]由python制定时,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]
Run Code Online (Sandbox Code Playgroud)

这就是我的理解状态。

jfs*_*jfs 5

p1.stdout.close()foo检测管道何时断裂(例如,何时p2过早退出)所必需的。

如果没有,p1.stdout.close()p1.stdout在父进程中保持打开状态,即使p2退出;p1不会知道没有人读取p1.stdoutp1即将继续写入,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()
Run Code Online (Sandbox Code Playgroud)

请参阅如何使用subprocess.Popen通过管道连接多个进程?