2011-04-07 70 views
21

我需要一個執行一個命令,產生大量的輸出並需要大量的時間來執行(> 30分鐘)。我正在考慮使用subprocess.Popen來做到這一點。我需要捕獲命令的輸出,所以我將PIPE傳遞給stdout和stderr。需要避免沒有通信的子進程死鎖

使用Popen.wait()時的死鎖問題在許多論壇上都有很好的記錄,所以Popen.communicate()是避免死鎖的建議方式。該解決方案的問題是通信()阻塞,直到命令完成。我需要打印執行命令時到達標準輸出的所有內容。如果20分鐘後沒有輸出,腳本執行將被終止。

這裏有一些限制,我需要尊重:

  • 我的Python版本是2.4.2,我不能升級。
  • 如果溶液仍然使用子,我需要通過subprocess.PIPE所有STD處理,以避免這個錯誤:http://bugs.python.org/issue1124861

有沒有辦法做到這一點?

+1

(從谷歌即將?)所有管道就會死鎖的喉管緩衝區的一個被填滿而沒有閱讀。例如當stderr被填充時stdout死鎖。切勿傳遞您不想閱讀的PIPE。 – 2014-05-07 11:07:07

回答

12

import os 
from subprocess import PIPE, STDOUT, Popen 

lines = [] 
p = Popen(cmd, bufsize=1, stdin=open(os.devnull), stdout=PIPE, stderr=STDOUT) 
for line in iter(p.stdout.readline, ''): 
     print line,   # print to stdout immediately 
     lines.append(line) # capture for later 
p.stdout.close() 
p.wait() 
+1

這就是我所需要的。非常感謝!你剛解決了一個問題,讓我花了整整一個工作日去調查! – GDICommander 2011-04-08 12:43:19

+2

@GDICommander:當心,代碼可能會在[Wine](http://www.winehq.org/)下放置stderr。它在Ubuntu上運行良好。確保在Windows上進行測試。 – jfs 2011-04-09 09:49:34

+0

如果我想發送輸入? 例如:p.stdin.write(「YES」) – 2014-07-24 14:47:24

6

你試過pexpect

+1

不幸的是,pexpect只適用於類Unix系統。我需要一個跨平臺的解決方案。 – GDICommander 2011-04-07 17:31:17

0

您可以考慮使用多個線程。分配一個線程從標準輸出讀取,一個從標準錯誤,並使用第三線程檢測超時:

while time.time() - last_output_time < 20 * 60: 
    time.sleep(20 * 60 - (time.time() - last_output_time)) 
print 'No output detected in the last 20 minutes. Terminating execution' 
sys.exit(1) 
1

爲了避免管道緩衝區填滿,只需啓動一個後臺線程的父進程。該線程可以連續讀取stdout(和stderr)以防止管道緩衝區填滿,也可以從中調用communicate()。無論哪種方式,主線程都可以繼續進行普通處理,並且子進程不會阻塞輸出操作。

將同步IO操作轉換爲異步操作(從主線程的角度來看)是線程的最佳用例之一。即使像Twisted這樣的異步框架有時也會使用它作爲最後的解決方案,因爲沒有本地異步接口可用於給定的操作。