2013-10-15 21 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

您不能將同一個管道發送到兩個不同的進程。或者,如果你這樣做了,他們最終會訪問同一個管道,這意味着如果一個進程讀取了某個內容,那麼它就不再可用。

你需要做的是以某種方式「發球」數據。


如果你不需要,因爲他們進來流中的數據,可以從p1閱讀所有的輸出,然後把它作爲兩者的輸入p2p3。這很容易:

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

如果你只需要p2p3並行運行,你可以運行它們每一個線程。

但是,如果您確實需要實時流式傳輸,則必須更仔細地連接事情。如果你能確定p2p3將總是消耗其輸入,而不是阻塞,比p1可以提供更快的速度,那麼你可以在沒有線程的情況下執行此操作(只是在p1.stdout.read()上循環),否則,你需要每個輸出線程消費者流程以及Queue或其他方式來傳遞數據。有關如何同步獨立線程的更多信息,請參閱source codecommunicate

+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'和線程都不會有幫助。當你從* same *進程讀取/寫入多個管道時(這種情況很容易發生死鎖),你可能會把它和情況混淆起來。 – jfs

+0

如果'p2'只是沒有從管道讀取,你的'p2.stdin.write'將會阻塞,直到它準備好。這意味着'p3'不會得到它的數據,因爲'p2'沒有準備好。而'p1'將會保持噴出數據直到它填滿管道。你可以用[this](http://pastebin.com/0a3PkhmK)之類的東西來解決這兩個問題(當然是以無限量的數據緩衝爲代價)。 – abarnert