2012-09-04 24 views
26

Possible Duplicate:
Wrap subprocess' stdout/stderr像往常一樣,你可以使python子進程輸出stdout和stderr,但也捕獲輸出爲字符串?

this questionhanan-n問是否有可能有一個python子輸出到標準輸出,同時保持輸出在後處理的字符串。在這種情況下,解決的辦法是遍歷所有的輸出線和手動打印出來:

output = [] 
p = subprocess.Popen(["the", "command"], stdout=subprocess.PIPE) 
for line in iter(p.stdout.readline, ''): 
    print(line) 
    output.append(line) 

然而,這種解決方案並不能概括要用於stdout和stderr做到這一點的情況下,同時滿足以下:

  • 從標準輸出/標準錯誤輸出應該去父進程的標準輸出/標準錯誤分別爲
  • 輸出應在儘可能實時地進行(但我只需要訪問字符串以結束)
  • o標準輸出和標準錯誤行之間不應該改變(我不太確定,如果子進程以不同的時間間隔刷新它的標準輸出和標準錯誤緩存,那麼它是如何工作的;現在讓我們來,我們得到包含完整線條很好的大塊的一切承擔?)

我通過subprocess documentation看去,卻找不到任何可以做到這一點。我能找到的最接近的是添加stderr=subprocess.stdout並使用與上述相同的解決方案,但是我們失去了正常輸出和錯誤之間的區別。有任何想法嗎?我猜測解決方案 - 如果有的話 - 將涉及異步讀取到p.stdoutp.stderr

這裏是我想要做什麼的例子:

p = subprocess.Popen(["the", "command"]) 
p.wait() # while p runs, the command's stdout and stderr should behave as usual 
p_stdout = p.stdout.read() # unfortunately, this will return '' unless you use subprocess.PIPE 
p_stderr = p.stderr.read() # ditto 
[do something with p_stdout and p_stderr] 
+0

我不明白這種技術(包裝任何IO函數和流對象)是不是泛化? – ninjagecko

+0

你如何建議我讓子進程實時打印到stdout和stderr,同時仍然在字符串中輸出結果,然後呢? – pflaquerre

回答

37

這個例子似乎爲我工作:

# -*- Mode: Python -*- 
# vi:si:et:sw=4:sts=4:ts=4 

import subprocess 
import sys 
import select 

p = subprocess.Popen(["find", "/proc"], 
    stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

stdout = [] 
stderr = [] 

while True: 
    reads = [p.stdout.fileno(), p.stderr.fileno()] 
    ret = select.select(reads, [], []) 

    for fd in ret[0]: 
     if fd == p.stdout.fileno(): 
      read = p.stdout.readline() 
      sys.stdout.write('stdout: ' + read) 
      stdout.append(read) 
     if fd == p.stderr.fileno(): 
      read = p.stderr.readline() 
      sys.stderr.write('stderr: ' + read) 
      stderr.append(read) 

    if p.poll() != None: 
     break 

print 'program ended' 

print 'stdout:', "".join(stdout) 
print 'stderr:', "".join(stderr) 

通常,當你想在同一時間做多的文件描述符的東西任何情況下,你不要不知道哪一個會有東西供你閱讀,你應該使用select或類似的東西(比如Twisted reactor)。

+1

由於'stdout'和'stderr'可能在我們調用'select'的時候都有數據,所以它的順序可能與原來的不一樣*完全相同,但這與我看到的解決方案非常接近遠。謝謝! – pflaquerre

+0

你對這個訂單有什麼意義?在我給出的例子中,如果有數據要從兩者中讀取,它將首先處理stdout,因爲它的fd通常會低於stderr,並且select應該按順序返回。如果不是這種情況或者不能保證,你可以簡單的改變代碼來做一些事情,比如ret [0]中的p.stdout.fileno():然後stderr也是一樣的,它應該保證順序。 –

+0

我正在討論流寫入的順序。例如,子進程可以先寫入stderr,然後再寫入其他內容到stdout,但是'select'仍然會在stderr之前從stdout讀取,這與實際發生的順序不同。在最糟糕的情況下,子進程可能會寫很多stdout,以至於stderr中的任何內容都會延遲,直到進程運行完畢。 (除非我誤解了選擇作品?) – pflaquerre

2

創建兩個讀者上面,一個stdout一個stderr並在一個新的線程啓動每個。這將以與進程輸出的順序大致相同的順序附加到列表中。如果你願意,可以維護兩個單獨的列表。

p = subprocess.Popen(["the", "command"]) 
t1 = thread.start_new_thread(func,stdout) # create a function with the readers 
t2 = thread.start_new_thread(func,stderr) 
p.wait() 
# your logic here 
+0

使用時間排序線程是一個壞主意。 – Dani

+0

@Dani - 那些是兩個不同的想法,一個不涉及線程的時間... – dfb

+0

他們說你可以用wait()和poll()與stdout(和stderr)的PIPE結合使用死鎖, 。 – danger89

9

要打印到控制檯和捕獲在一個可移植的方式字符串標準輸出/子進程的標準錯誤:

from StringIO import StringIO 

fout, ferr = StringIO(), StringIO() 
exitcode = teed_call(["the", "command"], stdout=fout, stderr=ferr) 
stdout = fout.getvalue() 
stderr = ferr.getvalue() 

其中teed_call()Python subprocess get children's output to file and terminal?

定義你可以使用任何類似文件的對象( .write()方法)。

+0

海報想要處理來自stdout/stderr的數據,而不是在命令結束時。 –

+0

@ThomasVanderStichele:點擊鏈接並查看'call()'定義。它儘快顯示stdout/stderr。它不是'subprocess.call()'。 – jfs

+6

這不起作用,至少並非總是如此:'AttributeError:StringIO實例沒有屬性'fileno'' – KomodoDave

相關問題