網頁

搜尋此網誌

2014年9月12日 星期五

subprocess 子行程管理

subprocess 是 Python  程式語言中用來新增子行程的模組,這是在 PEP 324 中所提出的建議,主要是統一 Python 新增子行程的方法,用來取代 os.system, os.spawn*, os.popen*, popen2.*, commands.* 等這些模組和函式。基本上,在  Python 中如果要新增子行程,你只要知道 subprocess 模組就行了

subprocess 模組最重要的是 Popen 類別,建立 Popen 物件時將會新增子行程去執行你要的工作,Popen 類別用起來比較精細,可操作的地方較多,相對較麻煩一些。因此 subprocess 模組提供下列三種方便的函式來新增子行程,注意到這三個函式是屬於同步呼叫 (synchronous call) 的方式,一定會等到子行程結束才執行下一行程式碼。
  • subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
    最簡單的子行程新增方式,執行並等待結束,回傳子行程的回傳值 (return code)。
  • subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
    呼叫子行程,並判斷回傳值是否為是否為 0,不然就引發 CalledProcessError 例外。
  • subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)
    呼叫子行程,與 subprocess.check_call 函式行為相似,但回傳 stdout 的字串值。
接下來介紹 Popen 類別,Popen 的建構子如下:
class subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

Popen 物件則有下列方法和屬性:
  • 方法
    • Popen.poll():判斷子行程是否結束,如果結束回傳 returncode 值,否則回傳 None 。
    • Popen.wait():等待子行程結束,回傳 returncode 值。
    • Popen.communicate(input=None):與子行程通訊,回傳 tuple 資料型態為 (stdoutdata, stderrdata),注意要通訊的 stdin, stdout, stderr 引數必須設定為 subprocess.PIPE 。
    • Popen.send_signal(signal):送信號給子行程。
    • Popen.terminate():終止子行程,送出 SIGTERM 信號。
    • Popen.kill():停止子行程,送出 SIGKILL 信號。
  • 屬性
    • stdin:如果建立 Popen 時的 stdin 引數是 subprocess.PIPE,那麼這個屬性會是 file object,否則為 None。
    • stdout:如果建立 Popen 時的 stdout 引數是 subprocess.PIPE,那麼這個屬性會是 file object,否則為 None。
    • stderr:如果建立 Popen 時的 stderr 引數是 subprocess.PIPE,那麼這個屬性會是 file object,否則為 None。
    • pid:子行程的 process ID。
    • returncode:子行程結束的回傳值,如果子行程還在執行沒有結束,returncode 會是 None 。
最後,注意 shell 引數若設定為 True 時,我們必須確定空白字元shell metacharacters 有被引號正確地包起來,不然建立子行程會錯誤或不如預期。預設的 shell=False 使 subprocess 模組會完整傳送所有字元,包含 shell metacharacters。看下列範例會更清楚

import subprocess
subprocess.check_output(['printf','Hello World\n'])
subprocess.check_output(['printf "Hello World\n"'], shell=True)
subprocess.check_output('printf "Hello World\n"', shell=True)

上述 3 行程式碼執行結果是相同的,差別在於 shell=True 時必須用引號處理空白和 metacharacters,最後一行則是因為序列 (sequence) 的引數只有一個,可以簡化成字串作為引數。

文件中建議,如果讓使用者輸入參數作為子行程的引數會有安全行的問題,容易發生 Shell injection 的危險,如果你很確定引數沒有任何疑慮,使用 shell=True 倒是讓程式碼更容易閱讀。

###

沒有留言:

張貼留言

熱門文章