2015-06-22 40 views
4

繼續from my previous question我看到,要獲取我在python中通過Popen生成的進程的錯誤代碼,我必須調用wait()或communic()(它可以用來訪問POPEN輸出和錯誤的屬性):Python Popen - 等待與通信vs CalledProcessError

app7z = '/path/to/7z.exe' 
command = [app7z, 'a', dstFile.temp, "-y", "-r", os.path.join(src.Dir, '*')] 
process = Popen(command, stdout=PIPE, startupinfo=startupinfo) 
out = process.stdout 
regCompressMatch = re.compile('Compressing\s+(.+)').match 
regErrMatch = re.compile('Error: (.*)').match 
errorLine = [] 
for line in out: 
    if len(errorLine) or regErrMatch(line): 
     errorLine.append(line) 
    if regCompressMatch(line): 
     # update a progress bar 
result = process.wait() # HERE 
if result: # in the hopes that 7z returns 0 for correct execution 
    dstFile.temp.remove() 
    raise StateError(_("%s: Compression failed:\n%s") % (dstFile.s, 
         "\n".join(errorLine))) 

然而the docs警告說wait()可能死鎖(當標準輸出=管,這是這裏的情況),而communicate()可能溢出。所以:

  1. 什麼是適當的東西在這裏使用?請注意,我確實使用輸出
  2. 我應該如何使用通信?難道是:

    process = Popen(command, stdout=PIPE, startupinfo=startupinfo) 
    out = process.communicate()[0] 
    # same as before... 
    result = process.returncode 
    if result: # ... 
    

    不確定阻止和內存錯誤

  3. 任何處理問題的更好/更Python的方式?我不認爲subprocess.CalledProcessError or the subprocess.check_call/check_output適用於我的情況 - 或者他們?

免責聲明:我沒有寫的代碼,我目前的維護者,因此質疑3.

相關:

我在Windows上,如果這有差別 - 蟒蛇2.7.8

應該有one--和最好只有一個--obvious辦法做到這一點

+0

不相關:您的行處理代碼可能被破壞,例如,「regErrMatch(line)」只被調用一次。 – jfs

+0

@JFSebastian:嘿謝謝 - 我認爲其意圖是'一旦'errorLine'中有東西,然後添加所有剩餘的行'(當我看到它時也困惑了我) - 可能一旦出現錯誤意味着所有其他的都會失敗 –

+0

要downvoter - ? –

回答

7
  • 有關死鎖:是安全使用stdout=PIPEwait()在一起iff您從管道讀取。 .communicate()做閱讀,併爲您
  • 有關內存調用wait():如果輸出可以是無限的,那麼你不應該使用.communicate(),其累積內存中的所有輸出。

在這裏使用什麼是正確的東西?

啓動子,逐行讀取它的輸出線,並等待它退出:

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

process = Popen(command, stdout=PIPE, bufsize=1) 
with process.stdout: 
    for line in iter(process.stdout.readline, b''): 
     handle(line) 
returncode = process.wait() 

此代碼不會死鎖由於有限OS管道緩衝區。此外,代碼還支持無限制輸出的命令(如果某條線路適合內存)。

iter()用於在子流程的stdout緩衝區刷新後立即讀取一行,以解決方法the read-ahead bug in Python 2。如果您不需要等待緩衝區填滿或子進程結束,就可以使用簡單的for line in process.stdout,只要它們在寫入時不需要立即讀取行。見Python: read streaming input from subprocess.communicate()

如果您知道該命令輸出可存放在內存中的所有情況,那麼你可以得到的輸出一下子:

它提出了CalledProcessError如果用非零退出命令返回狀態。在內部,check_output() uses Popen() and .communicate()

應該有one--和最好只有一個--obvious辦法做到這一點

subprocess.Popen()是,在許多情況下,許多工程的主要API。常用用例有Popen.communicate(),check_output(),0 check_call()等便利功能/方法。

有多種方法,因爲有多種不同的用例。

+0

好吧 - 我不明白的一件事就是iter中的行(process.stdout.readline,b''):'買我們而不是'在process.stdout中輸入': - 我跟着你的鏈接我瞭解synax但仍然...還需要關閉管道? (我猜是的) –

+0

@Mr_and_Mrs_D:用''語句關閉管道就像使用普通文件一樣。 'iter()'是解決[Python 2中的預讀錯誤]所必需的(https://bugs.python.org/issue3907) - 如果不需要「實時」輸出,則不需要。 – jfs

+0

非常好,非常感謝 - 我明白'with'關閉了管道(:D) - 我不確定是否需要它 - 請添加關於錯誤信息的答案,因爲這個錯誤是針對「一個顯而易見的方式」,而且這根本不明顯,爲什麼一個簡單的'輸入輸出'不會。非常感謝 –