2010-04-07 78 views
7

我想用一個python等效於在perl中管道一些shell命令。就像開放的Python版本(PIPE,「command |」)。Python的Popen清理

我去子模塊,並嘗試這個辦法:

p = subprocess.Popen("zgrep thingiwant largefile", shell=True, stdout=subprocess.PIPE) 

這適用於讀取輸出相同的方式,我在Perl會,但它不乾淨自己的身體。當我離開口譯員時,我得到了

grep: writing output: Broken pipe 

在幾百萬次的時間裏,我想我天真地希望所有這些都會爲我照顧,但事實並非如此。調用p終止或殺死似乎沒有幫助。看看進程表,我發現這會殺死/ bin/sh進程,但將孩子gzip留下來抱怨損壞的管道。

什麼是正確的方法來做到這一點?

+1

在子流程「p」完成之前,您是否正在退出解釋器? – physicsmichael 2010-04-07 22:52:06

回答

9

問題是pipe已滿。子進程停止,等待管道清空,但是你的進程(Python解釋器)退出,打破管道的末端(因此是錯誤信息)。

p.wait()不會幫助你:

警告如果子進程產生足夠的輸出到標準輸出或標準錯誤管道,使得它阻止等待操作系統管緩衝區,接受更多的數據。這將死鎖。使用communicate()來避免這種情況。

http://docs.python.org/library/subprocess.html#subprocess.Popen.wait

p.communicate()不會幫助你:

注意讀到的數據緩存在內存中,因此,如果數據量很大或無限不要使用此方法。

http://docs.python.org/library/subprocess.html#subprocess.Popen.communicate

p.stdout.read(num_bytes)不會幫助你:

警告使用communicate()而非.stdin.write.stdout.read.stderr.read避免死鎖由於任何其他操作系統管緩衝區溢出和阻止子進程。

http://docs.python.org/library/subprocess.html#subprocess.Popen.stdout

這個故事的寓意是,對於產量大,subprocess.PIPE也註定你一定失敗,如果你的程序試圖讀取數據(在我看來,你應該能夠把p.stdout.read(bytes)轉換成while p.returncode is None:循環,但上面的警告表明這可能會死鎖)。

該文檔建議與此替換殼管:

p1 = Popen(["zgrep", "thingiwant", "largefile"], stdout=PIPE) 
p2 = Popen(["processreceivingdata"], stdin=p1.stdout, stdout=PIPE) 
output = p2.communicate()[0] 

注意p2直接從p1採取它的標準輸入。這應該避免死鎖,但鑑於上述矛盾的警告,誰知道。不管怎麼說,如果最後一部分不適合你(它應該,儘管),你可以嘗試創建一個臨時文件,將第一次調用的所有數據寫入該文件,然後使用臨時文件作爲輸入到下一個進程。

0

你是怎麼執行這個過程的?

正確的方法是使用

p.communicate() 

見文檔的更多細節。

+0

即使我從不與進程進行通信,也會發生這種情況。只要創建對象p,然後退出解釋器就會導致此問題。 – 2010-04-07 20:29:47

+0

是的,如果我記得正確,Popen執行命令。 '溝通()'然後等待,直到進程結束,緩衝區被刷新等。等等。 另請參閱'check_call()'。 – Almad 2010-04-07 21:16:40

2

在打開的管道,你可以用命令輸出工作:p.stdout

for line in p.stdout: 
    # do stuff 
p.stdout.close() 
0

您需要wait的過程來完成:

import subprocess 
p = subprocess.Popen("cat /mach_kernel", shell=True) 
p.wait() 

或者,你可以捕捉到程序的標準輸出(像你一樣),也許它的標準錯誤,然後致電communicate

import subprocess 
p = subprocess.Popen("cat /mach_kernel", shell=True, 
        stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
stdout, stderr = p.communicate()