概述
python中一般推薦的執(zhí)行shell命令行的方式有兩種毅人,os.popen與subprocess.Popen话瞧。本文(python 3.7環(huán)境)說(shuō)明下兩者的使用舷手,關(guān)聯(lián)與差異眉尸。
os.popen
os.popen的函數(shù)簽名比較簡(jiǎn)單:
def popen(cmd, mode="r", buffering=-1):
其中?cmd?必須為 string 類型秒紧,mode只能為 r?或者 w栗恩,buffering參數(shù)與內(nèi)建函數(shù) open?一致透乾,就不敘述了。
其返回值為 stdin?或者 stdout的wrapper磕秤,wrapper中實(shí)現(xiàn)了迭代特性乳乌,因此可以使用像普通文件對(duì)象一樣遍歷內(nèi)容。
import os
dirs = os.popen('dir')
[dir for dir in dirs]
從參數(shù)上看市咆,popen只支持單工通信(read mode?或 write mode)汉操。
從內(nèi)部實(shí)現(xiàn)上來(lái)說(shuō),os.popen內(nèi)部調(diào)用了?subprocess.Popen函數(shù)蒙兰,調(diào)用如下:
if mode == "r":
????proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, bufsize=buffering)
????return _wrap_close(io.TextIOWrapper(proc.stdout), proc)
else:
????proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, bufsize=buffering)
????return _wrap_close(io.TextIOWrapper(proc.stdin), proc)
關(guān)于?subprocess.Popen?中的?shell 參數(shù)與 stdin/stdout參數(shù)我們會(huì)在后面分析磷瘤。
subprocess.Popen
Popen是一個(gè)類,并不是一個(gè)函數(shù)搜变。內(nèi)部實(shí)現(xiàn)比較復(fù)雜采缚,隔離了操作系統(tǒng)的差異。
def __init__(self, args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None, text=None):
其構(gòu)造函數(shù)相當(dāng)復(fù)雜痹雅,我們逐步進(jìn)行解析仰担。
這些參數(shù)中,部分分屬不同的操作系統(tǒng)绩社,Popen會(huì)做簡(jiǎn)單檢查后再進(jìn)行下一步處理摔蓝。
posix:preexec_fn
windows:startupinfo,createionflag
這些參數(shù)如果熟悉windows與Liunx創(chuàng)建進(jìn)程的API愉耙,應(yīng)該不會(huì)有什么疑問(wèn)贮尉,具體可以查看?MSDN?與?man?手冊(cè)。
args就是我們的命令行參數(shù)了朴沿,args可以是一個(gè)string猜谚,也可以是一個(gè)list败砂。如果是一個(gè)srting的話,Popen內(nèi)部會(huì)將其轉(zhuǎn)換成 list魏铅。
bufsize?與 os.popen的 buffering參數(shù)含義相同
executable?參數(shù)指的是需要運(yùn)行的可執(zhí)行文件昌犹,如果?這個(gè)參數(shù)不等于None,那么 args就作為參數(shù)傳遞給executable所指向的可執(zhí)行文件览芳。executable會(huì)受到shell參數(shù)的影響斜姥,后續(xù)我們會(huì)分析到。
stdin/stdout/strerr?主要用于輸入輸出的重定向功能沧竟。實(shí)參可以是?PIPE/DEVNULL/文件描述符/文件對(duì)象/None铸敏。
熟悉linux編程的朋友應(yīng)該都知道PIPE與devnull。如果輸入PIPE悟泵,那么Popen會(huì)在父進(jìn)程與子進(jìn)程之間創(chuàng)建輸入輸出的pipe用于相互通信杈笔。
如果實(shí)參是已經(jīng)打開的?文件描述符或文件對(duì)象時(shí),那么輸入輸出就會(huì)重定向到相應(yīng)的文件中糕非。
如果實(shí)參等于?None蒙具,那么直接使用?標(biāo)準(zhǔn)輸入輸出接口。
注意?bufsize?參數(shù)是針對(duì) stdin/stdout/stderr?的緩沖設(shè)置峰弹。
preexec_fn?參數(shù)只有在 posix環(huán)境中才有用店量。在?child_exec?執(zhí)行到?exec*?函數(shù)前會(huì)執(zhí)行?result = PyObject_Call(preexec_fn, preexec_fn_args_tuple, NULL);?作用顯而易見芜果,在child exec之前設(shè)置一個(gè)回調(diào)函數(shù)鞠呈,在這個(gè)特殊的時(shí)機(jī)做一些特殊的操作。
close_fds?這個(gè)參數(shù)感覺在posix環(huán)境中更有意義一些右钾,如果設(shè)置為True蚁吝,相當(dāng)于為0,1舀射,2以外的文件設(shè)置了?CLOSEXEC?標(biāo)志窘茁。在windows中則將默認(rèn)的文件繼承屬性設(shè)置為FALSE。
pass_fds 用于在父子進(jìn)程間傳遞文件句柄脆烟。在posix中山林,如果pass_fds不為None,那么close_fds必須為True邢羔。也就是說(shuō)pass_fds中的句柄會(huì)被關(guān)掉驼抹。
查看源碼的時(shí)候發(fā)現(xiàn)比較奇怪的地方,在posix中拜鹤,pass_fds?設(shè)置完繼承屬性后框冀,如果close_fds為True,那么所有的句柄都會(huì)被關(guān)掉敏簿,那么pass_fds還能正常繼承到子進(jìn)程中嗎明也?這里需要測(cè)試一下宣虾。
cwd是個(gè)人所共知的參數(shù),不細(xì)說(shuō)温数。只是要注意 cwd?會(huì)影響py腳本的查找與執(zhí)行绣硝。
restore_signals參數(shù)也是在posix中作用比較大,因?yàn)閣indows中信號(hào)功能實(shí)在太弱撑刺。具體功能就是將 SIGPIPE/SIGXFZ/SIGXFSZ?由SIG_IGN設(shè)置成?SIG_DFL域那。具體可以參考任意的linux參考書。
start_new_session?應(yīng)該只能在posix中使用猜煮,如果設(shè)置為True次员,那么子進(jìn)程在執(zhí)行過(guò)程中會(huì)調(diào)用 setsid,開啟一個(gè)新的會(huì)話王带。
env簡(jiǎn)單淑蔚,忽略不講。
encoding/errors/universal_newline/text都是與 stdin/stdout/stderr相關(guān)的參數(shù)愕撰。
結(jié)語(yǔ)
os.popen的參數(shù)簡(jiǎn)單刹衫,使用方便;subprocess.Popen參數(shù)復(fù)雜搞挣,能實(shí)現(xiàn)精細(xì)控制带迟。并且os.popen只能用于單工環(huán)境,即重定向stdin或者stdout文件囱桨,但是subprocess.Popen可以實(shí)現(xiàn) 0/1/2?三個(gè)描述符同時(shí)重定向仓犬。