2010-04-26 116 views
55

我正在使用Python的subprocess.communicate()從運行大約一分鐘的進程讀取stdout。Python:讀取來自subprocess.communicate()的流輸入

如何以流方式打印出該進程的stdout的每一行,以便我可以在輸出生成時看到輸出,但在繼續之前仍會阻止進程終止?

subprocess.communicate()似乎一次給所有的輸出。

+0

相關:[獲取實時輸出使用子過程](http://stackoverflow.com/q/803265/4279) – jfs 2014-10-16 20:11:54

回答

36

請注意,我認爲J.F. Sebastian's method (below)更好。


下面是一個簡單的例子(沒有進行錯誤檢查):

import subprocess 
proc = subprocess.Popen('ls', 
         shell=True, 
         stdout=subprocess.PIPE, 
         ) 
while proc.poll() is None: 
    output = proc.stdout.readline() 
    print output, 

如果ls結束得太快,那麼您已經閱讀所有的數據之前,while循環可能會結束。

您可以趕上在標準輸出餘這樣說:

output = proc.communicate()[0] 
print output, 
+1

該方案是否成爲python doc引用的緩衝區阻塞問題的受害者? – 2010-04-26 19:22:08

+0

@海因裏希,緩衝區阻塞問題不是我理解得很好。我相信(僅僅從google搜索),只有當你沒有從while循環中讀取stdout(和stderr?)時纔會出現這個問題。所以我認爲上面的代碼是可以的,但我不能肯定地說。 – unutbu 2010-04-26 19:44:44

+1

這實際上也從一個阻塞問題的困擾,幾年前我沒有結束麻煩,其中的readline將阻止,直到它得到了即使PROC結束了一個換行符。我不記得了解決方案,但我認爲它有事情做與做讀取一個工作線程,只是循環'而proc.poll()是無:time.sleep(0)'或諸如此類的話。基本上,您需要確保輸出換行符是流程所做的最後一件事(因爲您不能讓解釋器時間再次循環),或者您需要做一些「花哨」的事情。 – 2010-04-26 20:05:32

3

如果你想要一個非阻塞的方法,不要使用process.communicate()。如果將subprocess.Popen()參數stdout設置爲PIPE,則可以從process.stdout中讀取並檢查過程是否仍使用process.poll()運行。

+1

[非阻塞方法不是直接的(http://stackoverflow.com/q/375427/4279) – jfs 2015-09-23 14:45:34

6

我相信從以流方式的過程收集輸出最簡單的方法是這樣的:

import sys 
from subprocess import * 
proc = Popen('ls', shell=True, stdout=PIPE) 
while True: 
    data = proc.stdout.readline() # Alternatively proc.stdout.read(1024) 
    if len(data) == 0: 
     break 
    sys.stdout.write(data) # sys.stdout.buffer.write(data) on Python 3.x 

readline()read()函數應該只在EOF結束後返回一個空字符串,否則它將在沒有任何內容需要讀取時阻塞(readline()包含換行符,所以在空行上,它返回「\ n」)。這避免了在循環之後需要一個尷尬的最終communicate()調用。

對於具有很長行read()的文件,可能更適合減少最大內存使用量 - 傳遞給它的數量是任意的,但排除它會導致一次讀取整個管道輸出,這可能不合需要。

+1

'data = proc.stdout.read()'阻塞,直到讀取所有*數據。你可能會把它與os.read(fd,maxsize)混淆起來,它可以提前返回(只要有數據可用)。 – jfs 2013-08-22 09:15:24

+0

你是對的,我錯了。但是,如果將合理數量的字節作爲參數傳遞給'read()',那麼它可以正常工作,並且只要最大行長度合理,'readline()'也可以正常工作。相應地更新我的答案。 – 2013-08-22 23:46:34

99

儘快子進程刷新其標準輸出緩衝區獲得由線子輸出線:

#!/usr/bin/env python2 
from subprocess import Popen, PIPE 

p = Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1) 
with p.stdout: 
    for line in iter(p.stdout.readline, b''): 
     print line, 
p.wait() # wait for the subprocess to exit 

iter()來,儘快爲他們寫要解決the read-ahead bug in Python 2讀線。

如果子進程的stdout在非交互模式下使用塊緩衝而不是行緩衝(導致輸出延遲,直到孩子的緩衝區已滿或被孩子明確刷新),那麼您可以嘗試強制使用pexpect, pty modulesunbuffer, stdbuf, script utilities無緩衝輸出,見Q: Why not just use a pipe (popen())?


這裏的Python 3中的代碼:

#!/usr/bin/env python3 
from subprocess import Popen, PIPE 

with Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1, universal_newlines=True) as p: 
    for line in p.stdout: 
     print(line, end='') 

注意:與輸出子過程的Python 2字節串原樣; Python 3使用文本模式(cmd的輸出使用locale.getpreferredencoding(False)編碼進行解碼)。

+0

b「是什麼意思? – Aaron 2014-04-07 18:58:20

+3

'b'''是Python 2.7和Python 3中的'bytes'文字。 – jfs 2014-04-07 18:59:03

+0

'bufsize = 1'是關鍵!至少它在cmd是'fastboot'時起作用。 – 2014-08-03 20:50:14

2

如果你只是想通過實時傳遞輸出,很難得到比這更簡單:

import subprocess 

# This will raise a CalledProcessError if the program return a nonzero code. 
# You can use call() instead if you don't care about that case. 
subprocess.check_call(['ls', '-l']) 

docs for subprocess.check_call()

如果你需要處理它的輸出,當然,循環。但如果你不這樣做,那就保持簡單。

編輯:J.F. Sebastian指出二者,對於輸出和錯誤參數的缺省值通過sys.stdout來和sys.stderr,那如果sys.stdout的和sys.stderr已被替換,這將失敗(比如,用於在測試中捕獲輸出)。

+0

如果將'sys.stdout'或'sys.stderr'替換爲沒有真正的fileno()的文件類對象,它將不起作用。如果'sys.stdout','sys.stderr'沒有被替換,那麼它更簡單:'subprocess.check_call(args)'。 – jfs 2015-09-22 18:47:24

+0

謝謝!我意識到替換sys.stdout/stderr的變幻莫測,但不知何故,如果你忽略參數,它會將stdout和stderr傳遞給正確的地方。除非我想要'CalledProcessError',否則我喜歡'call()'通過'check_call()'。 – Nate 2015-09-23 14:41:29

+0

'python -mthis':*「錯誤不應該默默通過。」 除非明確聲明爲無效。「*這就是爲什麼_example code_應該比'call()'更適合'check_call()'的原因。 – jfs 2015-09-23 14:42:43

0
myCommand="ls -l" 
cmd=myCommand.split() 
# "universal newline support" This will cause to interpret \n, \r\n and \r  equally, each as a newline. 
p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True) 
while True:  
    print(p.stderr.readline().rstrip('\r\n')) 
+1

解釋你的解決方案只是爲了讓人們更好地理解,總是很好 – DaFois 2017-11-12 23:44:50