Предыдущие ответы упустили важный момент. Замена конвейера оболочки в основном правильная, как указывает geocar. почти достаточно для запуска communicate
на последнем элементе трубы.
Остается проблема с передачей входных данных в конвейер. С несколькими подпроцессами простой communicate(input_data)
на последнем элементе не работает - он зависает навсегда. Вам нужно вручную создать конвейер и дочерний элемент, например:
import os
import subprocess
input = """\
input data
more input
""" * 10
rd, wr = os.pipe()
if os.fork() != 0: # parent
os.close(wr)
else: # child
os.close(rd)
os.write(wr, input)
os.close(wr)
exit()
p_awk = subprocess.Popen(["awk", "{ print $2; }"],
stdin=rd,
stdout=subprocess.PIPE)
p_sort = subprocess.Popen(["sort"],
stdin=p_awk.stdout,
stdout=subprocess.PIPE)
p_awk.stdout.close()
out, err = p_sort.communicate()
print (out.rstrip())
Теперь дочерний элемент обеспечивает ввод через канал, а родительские вызовы взаимодействуют (), что работает, как и ожидалось. При таком подходе вы можете создавать произвольные длинные конвейеры, не прибегая к «делегированию части работы оболочке». К сожалению, документация подпроцесса 1015 * не упоминает об этом.
Есть способы добиться того же эффекта без труб:
from tempfile import TemporaryFile
tf = TemporaryFile()
tf.write(input)
tf.seek(0, 0)
Теперь используйте stdin=tf
для p_awk
. Это вопрос вкуса, что вы предпочитаете.
Выше все равно не на 100% эквивалентно bash-конвейерам, потому что обработка сигналов отличается. Это можно увидеть, если добавить еще один элемент трубы, который усекает вывод sort
, например head -n 10
. С кодом выше, sort
выведет сообщение об ошибке «Сломанная труба» на stderr
. Вы не увидите это сообщение при запуске того же конвейера в оболочке. (Это единственное отличие, результат в stdout
тот же). Кажется, причина в том, что Popen
Python устанавливает SIG_IGN
для SIGPIPE
, тогда как оболочка оставляет его на SIG_DFL
, и обработка сигналов sort
отличается в этих двух случаях.