Day 2. 執行外部指令
碎碎念:這幾天瑣事好多,結果自行放假兩天。或許之後六日也都停更感覺比較不會那麼累orz.
早在還沒碰python之前。有時候載別人套件的source code下來自己compile時,會發現他們用了一些python的scripts來輔助使用者安裝。
當時想說,既然會額外用python來做這些輔助作業,而不是使用大多Unix類作業系統都會有的shell script來做這件事(雖然shell又分好幾種就是...),想必有他獨到的地方。
所以研究怎麼利用python來做這些事情就變成個人優先想了解的部分。
subprocess
和前面與其他語言binding的部分不同,呼叫外部的指令好像主要都是用subprocess來做了。
不論是從網路上查到的資料,或是python的官方文件來看。
(This module intends to replace several older modules and functions)
能夠開啟一個child process的方法大致上有:
- subprocess.run (python 3.5 ~)
- subprocess.call
- subprocess.check_call / subprocess.check_output
- subprocess.Poepn
從找到的這篇來看What's the difference between Python's subprocess.call and subprocess.run,
可以知道run是從3.5版開始才有的東西,目的也許是提供比較全面的功能,
整合目前的call / check_call / check_output。
上面都是高階的函式,Popen則是低階的。
subprocess.run
subprocess.run的用法大致如下:
import subprocess as sp
# args: 預設型別是list.
sp.run(['ls', '-l'])
# 將 args 作為 /bin/bash 的參數
sp.run( 'ls -l', shell=True )
# 取得output, 用pipe把資料轉到程式內:
out = sp.run(['ls', '-l'], stdout=sp.PIPE)
# 讀檔:
with open( './data.txt', 'r' ) as file:
sp.run(['cat'], stdin=file)
# 若要從程式內輸入資料,type必須為bytes:
msg = 'hello, world\n'
sp.run(['cat'], input=msg.encode())
還有其他沒寫到的有
- run( ..., check = True ): 效力等同於 check_call (除了沒input參數)
- run( ..., timeout = sec ): 執行時間過多久timeout.
- run預設的stdin, stdout, stderr都是None.
subprocess.call
- subprocess.call基本上和run差不多,主要差在少了input這個參數不能用而已。
- subprocess.check_call 和call差不多,不同點在回傳狀態不為0則會丟例外。
- subprocess.check_output 和check_call差不多,差在他會回傳stdout的結果。
- subprocess.Popen
Popen提供許多管理subprocess的低階方法,這也代表他有很多參數可以調整。
這邊只複製別人整理需要用到的、和run, call這些方法不同的地方。
Popen的Constructor的參數和run其實差不多。
以前的文章中有人提到,其實call和Popen的差別只在於: call在construct結束後會呼叫Popen.wait().
但單獨使用Popen.wait的時候有一點要小心的是,由於他會等程式回傳正常結束的狀態值,所以如果前面有指定stdout=PIPE, stderr=PIPE可能會造成deadlock。
因此若要取得stdout, stderr, 最好是呼叫communicate來做這件事情。
import subprocess as sp
# Input from file:
with open('./data.txt', 'r') as file:
p = sp.Popen( ['cat'], stdin=file, stdout=sp.PIPE, stderr=sp.PIPE )
out, err = p.communicate()
# Input from python:
p = sp.Popen( ['cat'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE )
out, err = p.communicate( input=b'hello, world\n' )
參考資料:
- 官方文件: subprocess
- 在電梯裡遇見雙胞胎(blog): Shell Scripting in Python
- aweimeow's blog: Python subprocess 各函式的使用時機
備註:
不知為何,總覺得python真的還滿好玩的。