Соединяют несколько подпроцессов

У меня есть 5 процессов p1,p2,...,p5, где я хочу записать некоторые данные в stdin p1, pipe p1 output to p2 stdin и, наконец, прочитать окончательный результат с вывода 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 script для этого (который я уже написал и работает), но я хотел бы знать, как достичь этого с Python.

Итак, я хотел бы спросить, как сделать все это правильно, в частности, в каком порядке выполнять операции чтения/записи/закрытия на трубах?

Я работаю над 64-разрядным Linux, если это имеет значение.

EDIT: Я забыл упомянуть, что все процессы p1,.. p5 потребляют все введенные им данные, обрабатывают их, записывают в stdout, а затем завершают. Таким образом, процессы, следующие в конвейере, не должны заканчиваться до того, как предыдущие завершили обработку.

EDIT2: Я знаю, что я мог бы также использовать

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

но мой главный интерес заключается в том, чтобы научиться цеплять трубы исключительно в коде python.

Ответы

Ответ 1

Возможно, это может помочь:

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

Надеюсь, это может быть hepfull.