2013-10-15 3 views
2

Я знаю, как делать несколько «вложенных» труб с использованием подпроцессов, однако у меня есть еще одно сомнение. Я хочу сделать следующее:Подпроцессы Python с несколькими трубами

p1=Popen(cmd1,stdout=PIPE) 
p2=Popen(cmd2,stdin=p1.stdout) 
p3=Popen(cmd3,stdin=p1.stdout) 

Учтите, что p3 использует p1.stdout вместо p2.stdout. Проблема в том, что после выполнения p2 p1.stdout пуст. Пожалуйста, помогите мне!

ответ

2

Вы не можете отправить один и тот же трубопровод в два разных процесса. Или, скорее, если вы это сделаете, они получат доступ к одному и тому же трубу, что означает, что если один процесс что-то читает, он больше не доступен для другого.

Что вам нужно сделать, это «tee» данные в некотором роде.


Если вам не нужно, чтобы поток данных, как они приходят, вы можете прочитать все выходные данные из p1, а затем отправить его в качестве входных данных как p2 и p3. Это очень просто:

output = check_output(cmd1) 
p2 = Popen(cmd2, stdin=PIPE) 
p2.communicate(output) 
p3 = Popen(cmd3, stdin=PIPE) 
p3.communicate(output) 

Если вам просто нужно p2 и p3 работать параллельно, вы можете просто запустить их каждый в потоке.

Но если вам действительно нужна потоковая передача в реальном времени, вам нужно подключить все более тщательно. Если вы можете быть уверены, что p2 и p3 всегда будут потреблять их вход, без блокировки, то быстрее, чем p1 может предоставить его, вы можете сделать это без потоков (только цикл на p1.stdout.read()), но в противном случае вам потребуется выходной поток для каждого потребительский процесс и Queue или каким-либо другим способом передачи данных. См. source code - communicate, чтобы узнать больше о том, как синхронизировать отдельные потоки.

+0

Спасибо большое, он работал с check_output. – fuchini

0

Если вы хотите скопировать выход из подпроцесса на другие процессы, не читая весь вывод сразу, то вот реализация @abarnert's suggestion к перебрать p1.stdout, который достигает этого:

from subprocess import Popen, PIPE 

# start subprocesses 
p1 = Popen(cmd1, stdout=PIPE, bufsize=1) 
p2 = Popen(cmd2, stdin=PIPE, bufsize=1) 
p3 = Popen(cmd3, stdin=PIPE, bufsize=1) 

# "tee" the data 
for line in iter(p1.stdout.readline, b''): # assume line-oriented data 
    p2.stdin.write(line) 
    p3.stdin.write(line) 

# clean up 
for pipe in [p1.stdout, p2.stdin, p3.stdin]: 
    pipe.close() 
for proc in [p1, p2, p3]: 
    proc.wait() 
+0

За исключением того, что если вы не можете быть уверены, что 'p2' или' p3' никогда не будут блокироваться или занять слишком много времени, чтобы обслуживать этот канал, это затормозит. Чтобы сделать это безопасно, вам действительно нужно либо «выбрать», либо три потока. – abarnert

+0

@abarnert: Я не вижу, как вы могли бы получить тупик здесь, если процессы не ведут себя патологически, в этом случае ни 'select', ни нити не помогут. Возможно, вы сбиваете с толку случай, когда вы читаете/пишете несколько труб из того же * процесса (в этом случае легко затормозить). – jfs

+0

Если 'p2' просто не читает из своего канала, ваш' p2.stdin.write' будет блокироваться, пока он не будет готов. Это означает, что 'p3' не получит свои данные, потому что' p2' не готов. И 'p1' будет просто продолжать извергать данные, пока не заполнит трубу. Вы можете исправить обе эти проблемы (конечно, за счет буферизации неограниченных объемов данных) с чем-то вроде [this] (http://pastebin.com/0a3PkhmK). – abarnert

Смежные вопросы