Background photo by Artturi Jalli on Unsplash
撰寫程式專案時,常需要在父進程下創建子進程進行支線任務,例如在shell script(.sh)中,會在 linux command 的基礎下,包含 python script, R script 的執行,透過串聯輸出\輸入檔案的方式,產生目標內容。
同理我們也能在以 python 做為父進程的情況下,以 subprocess 模組協助執行外部指令(子進程)、串接資料輸出\輸入的管道、並取得返回值,本文將介紹 subprocess 模組管理子進程的方法與程式碼。
Part1: subprocess 套件引入
subprocess 為 Python 內建模組,引入即可使用
|
|
Part2: 基本函數: run() and Popen()
subprocess 啟動子進程的方式有兩種 subprocess.run()
和 subprocess.Popen
其中run()
在大部分情況下都可使用,Popen()
則使用於更進階的底層操作
兩函數的基本參數都一樣,下面以 run()
來做參數介紹:
subprocess.run()
- 外部指令的參數有兩種輸入方式,但建議以list形式輸入:
可以使用
shlex
套件協助將指令 string 切分成 list 注意:shell = True
代表允許系統調用shell執行,如同讓主機門戶大開、容易發生 shell injection 的資安風險,請盡量少用
|
|
- default 情況下,
run()
執行無誤 (returncode = 0) 後會返回subprocess.CompletedProcess
的實例並印出結果;如果想查看返回狀況,可加上check = True
, 當 returncode != 0 會抛出CalledProcessError
- default 情況下,
run()
將結果直接印出,若想抓取輸出並存成變數,可使用capture_output = True
- default 輸出形式為 bytes,可用
text = True
來更改輸出形式,透過下面例子來比較:
|
|
subprocess.Popen()
大部分的參數跟 run()
相同,特別的是只有 Popen()
可使用 PIPE
在串聯子進程間的 stdout, stdin, stderr,並搭配 communicate()
防止死瑣,詳細內容在Part3
注意: run()不能使用 PIPE 和 communicate()
Part3: 以 PIPE 串聯進程間的輸入\ 輸出
PIPE 觀念相當於 linux 裡的 piping |
,可減少中間檔案儲存的冗餘。
下面的例子利用 PIPE 將第一個子進程的輸出,當作第二個子進程的輸入
|
|
PIPE 的實用程度高,但當管道內暫存的資料量過大,會造成子進程卡住,永遠無法結束(稱作死瑣),使用 communicate() 可即時讀取 PIPE 內容:
|
|
另外也可將 PIPE 的輸出存進檔案,解決死瑣問題
|
|
Part4: 實例應用
Example1:
我想檢查幾份log檔是否有回報錯誤訊息,利用 PIPE
加上 communicate()
將子進程 grep 到的內容存進 out 變數、並判斷是否有錯誤訊息
|
|
Example2:
現在有一份vcf原始檔,需要先以bio-tool(using Singularity)做格式轉換,再用python做資料篩選,但我不想產生轉換格式的中間檔,於是寫了以下 python script,以原始vcf做input,先開一個子進程處理vcf,以PIPE儲存輸出,存進 pyton 變數並作後續篩選
|
|
參考資料
https://docs.python.org/zh-tw/3/library/subprocess.html# https://www.aikaiyuan.com/4705.html