python執(zhí)行shell命令行

概述

python中一般推薦的執(zhí)行shell命令行的方式有兩種,os.popen與subprocess.Popen。本文(python 3.7環(huán)境)說明下兩者的使用,關(guān)聯(lián)與差異。

os.popen

os.popen的函數(shù)簽名比較簡單:

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)了迭代特性,因此可以使用像普通文件對象一樣遍歷內(nèi)容。

import os

dirs = os.popen('dir')

[dir for dir in dirs]

從參數(shù)上看,popen只支持單工通信(read mode?或 write mode)。

從內(nèi)部實(shí)現(xiàn)上來說,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ù)我們會在后面分析。

subprocess.Popen

Popen是一個類,并不是一個函數(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會做簡單檢查后再進(jìn)行下一步處理。

posix:preexec_fn

windows:startupinfo,createionflag

這些參數(shù)如果熟悉windows與Liunx創(chuàng)建進(jìn)程的API,應(yīng)該不會有什么疑問,具體可以查看?MSDN?與?man?手冊。

args就是我們的命令行參數(shù)了,args可以是一個string,也可以是一個list。如果是一個srting的話,Popen內(nèi)部會將其轉(zhuǎn)換成 list。

bufsize?與 os.popen的 buffering參數(shù)含義相同

executable?參數(shù)指的是需要運(yùn)行的可執(zhí)行文件,如果?這個參數(shù)不等于None,那么 args就作為參數(shù)傳遞給executable所指向的可執(zhí)行文件。executable會受到shell參數(shù)的影響,后續(xù)我們會分析到。

stdin/stdout/strerr?主要用于輸入輸出的重定向功能。實(shí)參可以是?PIPE/DEVNULL/文件描述符/文件對象/None。

熟悉linux編程的朋友應(yīng)該都知道PIPE與devnull。如果輸入PIPE,那么Popen會在父進(jìn)程與子進(jìn)程之間創(chuàng)建輸入輸出的pipe用于相互通信。

如果實(shí)參是已經(jīng)打開的?文件描述符或文件對象時,那么輸入輸出就會重定向到相應(yīng)的文件中。

如果實(shí)參等于?None,那么直接使用?標(biāo)準(zhǔn)輸入輸出接口。

注意?bufsize?參數(shù)是針對 stdin/stdout/stderr?的緩沖設(shè)置。

preexec_fn?參數(shù)只有在 posix環(huán)境中才有用。在?child_exec?執(zhí)行到?exec*?函數(shù)前會執(zhí)行?result = PyObject_Call(preexec_fn, preexec_fn_args_tuple, NULL);?作用顯而易見,在child exec之前設(shè)置一個回調(diào)函數(shù),在這個特殊的時機(jī)做一些特殊的操作。

close_fds?這個參數(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。也就是說pass_fds中的句柄會被關(guān)掉。

查看源碼的時候發(fā)現(xiàn)比較奇怪的地方,在posix中,pass_fds?設(shè)置完繼承屬性后,如果close_fds為True,那么所有的句柄都會被關(guān)掉,那么pass_fds還能正常繼承到子進(jìn)程中嗎?這里需要測試一下。

cwd是個人所共知的參數(shù),不細(xì)說。只是要注意 cwd?會影響py腳本的查找與執(zhí)行。

restore_signals參數(shù)也是在posix中作用比較大,因?yàn)閣indows中信號功能實(shí)在太弱。具體功能就是將 SIGPIPE/SIGXFZ/SIGXFSZ?由SIG_IGN設(shè)置成?SIG_DFL。具體可以參考任意的linux參考書。

start_new_session?應(yīng)該只能在posix中使用,如果設(shè)置為True,那么子進(jìn)程在執(zhí)行過程中會調(diào)用 setsid,開啟一個新的會話。

env簡單,忽略不講。

encoding/errors/universal_newline/text都是與 stdin/stdout/stderr相關(guān)的參數(shù)。

結(jié)語

os.popen的參數(shù)簡單,使用方便;subprocess.Popen參數(shù)復(fù)雜,能實(shí)現(xiàn)精細(xì)控制。并且os.popen只能用于單工環(huán)境,即重定向stdin或者stdout文件,但是subprocess.Popen可以實(shí)現(xiàn) 0/1/2?三個描述符同時重定向。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容