将多个子进程连接起来

14 投票
1 回答
9146 浏览
提问于 2025-04-16 19:31

我有5个进程 p1,p2,...,p5,我想把一些数据写入到p1的标准输入,然后把p1的输出传给p2的标准输入,最后从p5的输出中读取最终结果。

我到目前为止尝试过的内容:

p1 = Popen(['p1'], stdin=PIPE, stdout=PIPE)
p2 = Popen(['p2'], stdin=p1.stdout, stdout=PIPE)
...
p5 = Popen(['p5'], stdin=p4.stdout, stdout=PIPE)

# write data to stdin
p1.stdin.write(indata)
p1.stdin.close()

# not sure in what order to close the pipes here, if at all

# read output
out = p5.stdout.read()
print out

最后那段代码一直卡在那里,因为我可能在读写操作上做错了。

我能成功地让单个进程工作,使用了 communicate(),还有两个进程的例子,但没有给第一个进程提供任何输入(这是Python文档中的例子):

output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]

但是我搞不清楚,怎么给第一个进程提供输入而不让解释器卡住。

我也可以用一个bash脚本来实现这个(我已经写好了并且可以工作),但我想知道怎么用Python做到同样的事情。

所以,我想问一下,怎么才能正确地做到这一切,特别是关于在管道上进行读/写/关闭操作的顺序?

我在64位的Linux系统上工作,如果这有影响的话。

补充说明:我忘了提到,所有的进程p1到p5都会消耗它们接收到的所有输入,处理这些输入,然后写入标准输出,最后结束。因此,管道中的下一个进程在前一个进程处理完之前不应该结束。

补充说明2:我知道我也可以使用

command = 'bash -c "p1 | p2 | p3 | p4 | p5"'
proc = Popen([command], shell=True)
out, err = proc.communicate(input=indata)
print out

但我主要想知道如何在纯Python代码中连接这些管道。

1 个回答

10

也许这可以帮到你:

import sys
import tempfile
from subprocess import Popen, PIPE


cmd = [sys.executable, '-c', 'print raw_input()']

# Using a temp file to give input data to the subprocess instead of stdin.write to avoid deadlocks.
with tempfile.TemporaryFile() as f:
    f.write('foobar')
    f.seek(0)  # Return at the start of the file so that the subprocess p1 can read what we wrote.
    p1 = Popen(cmd, stdin=f, stdout=PIPE)

p2 = Popen(cmd, stdin=p1.stdout, stdout=PIPE)
p3 = Popen(cmd, stdin=p2.stdout, stdout=PIPE)

# No order needed.
p1.stdout.close()
p2.stdout.close()

# Using communicate() instead of stdout.read to avoid deadlocks. 
print p3.communicate()[0]

输出结果:

$ python test.py
foobar

希望这能对你有帮助。

撰写回答