在管道中使用Python subprocess模块的stdout.close()
在Python的子进程模块中,如果我们想要在Python里运行一个命令行指令,
foo | grep bar
我们可能会使用
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]
这就是我目前的理解状态。
1 个回答
8
p1.stdout.close()
是让 foo
知道管道什么时候断开的关键,比如说 p2
提前退出的时候。
如果不调用 p1.stdout.close()
,那么 p1.stdout
在父进程中会一直保持打开状态,即使 p2
退出了;p1
也不会知道没有人再读取 p1.stdout
,所以 p1
会继续往 p1.stdout
写数据,直到操作系统的管道缓冲区满了,然后就会一直卡在那里。
如果想要模拟 foo | grep bar
这个命令,但不使用命令行的话,可以这样做:
#!/usr/bin/env python3
from subprocess import Popen, PIPE
with Popen(['grep', 'bar'], stdin=PIPE) as grep, \
Popen(['foo'], stdout=grep.stdin):
grep.communicate()