Python中可以執(zhí)行shell命令的相關(guān)模塊和函數(shù)有:
os.system
os.spawn*
os.popen* --廢棄
popen2.* --廢棄
commands.* --廢棄浅蚪,3.x中被移除
隨著Python版本的更新,過多的模塊引起代碼的復(fù)雜與冗余意乓,因此Python新引入了一個(gè)模塊subprocess劳闹,將以上幾個(gè)模塊中的功能集中到它當(dāng)中,以后我們只需import這一個(gè)即可洽瞬。
1本涕、subprocess模塊
subprocess的目的就是啟動(dòng)一個(gè)新的進(jìn)程并且與之通信。
- 運(yùn)行python的時(shí)候伙窃,我們都是在創(chuàng)建并運(yùn)行一個(gè)進(jìn)程菩颖。像Linux進(jìn)程那樣,一個(gè)進(jìn)程可以fork一個(gè)子進(jìn)程为障,并讓這個(gè)子進(jìn)程exec另外一個(gè)程序晦闰。在Python中,我們通過標(biāo)準(zhǔn)庫(kù)中的subprocess包來fork一個(gè)子進(jìn)程鳍怨,并運(yùn)行一個(gè)外部的程序呻右。
- subprocess包中定義有數(shù)個(gè)創(chuàng)建子進(jìn)程的函數(shù),這些函數(shù)分別以不同的方式創(chuàng)建子進(jìn)程鞋喇,所以我們可以根據(jù)需要來從中選取一個(gè)使用声滥。另外subprocess還提供了一些管理標(biāo)準(zhǔn)流(standard stream)和管道(pipe)的工具,從而在進(jìn)程間使用文本通信侦香。
1.1 call
父進(jìn)程等待子進(jìn)程執(zhí)行命令落塑,返回子進(jìn)程執(zhí)行命令的狀態(tài)碼(returncode,相當(dāng)于Linux exit code)罐韩,如果出現(xiàn)錯(cuò)誤憾赁,不進(jìn)行報(bào)錯(cuò)。
- 這里說的返回執(zhí)行命令的狀態(tài)碼的意思是:如果我們通過一個(gè)變量 res = subprocess.call(['dir',shell=True]) 獲取的執(zhí)行結(jié)果散吵,我們能獲取到的是子進(jìn)程執(zhí)行命令執(zhí)行結(jié)果的狀態(tài)碼龙考,即res=0/1 執(zhí)行成功或者不成功,并不代表說看不到執(zhí)行結(jié)果矾睦,在Python的console界面中我們是能夠看到命令結(jié)果的晦款,只是獲取不到。想獲取執(zhí)行的返回結(jié)果顷锰,請(qǐng)看check_output柬赐。
- 不進(jìn)行報(bào)錯(cuò)解釋:如果我們執(zhí)行的命令在執(zhí)行時(shí),操作系統(tǒng)不識(shí)別官紫,系統(tǒng)會(huì)返回一個(gè)錯(cuò)誤肛宋,如:abc命令不存在,這個(gè)結(jié)果會(huì)在console界面中顯示出來束世,但是我們的Python解釋器不會(huì)提示任何信息酝陈,如果想讓Python解釋器也進(jìn)行報(bào)錯(cuò),請(qǐng)看check_call
示例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import subprocess
print "1. ################## subprocess.call ###############"
print u"call方法調(diào)用系統(tǒng)命令進(jìn)行執(zhí)行毁涉,如果出錯(cuò)不報(bào)錯(cuò)"
subprocess.call(['dir'],shell=True)
注:shell默認(rèn)為False沉帮,在Linux下,shell=False時(shí), Popen調(diào)用os.execvp()執(zhí)行args指定的程序;shell=True時(shí)穆壕,如果args是字符串待牵,Popen直接調(diào)用系統(tǒng)的Shell來執(zhí)行args指定的程序,如果args是一個(gè)序列喇勋,則args的第一項(xiàng)是定義程序命令字符串缨该,其它項(xiàng)是調(diào)用系統(tǒng)Shell時(shí)的附加參數(shù)。
在Windows下川背,不論shell的值如何贰拿,Popen調(diào)用CreateProcess()執(zhí)行args指定的外部程序。如果args是一個(gè)序列熄云,則先用list2cmdline()轉(zhuǎn)化為字符串膨更,但需要注意的是,并不是MS Windows下所有的程序都可以用list2cmdline來轉(zhuǎn)化為命令行字符串缴允。在windows下荚守,調(diào)用腳本時(shí)要寫上shell=True。
1.2 check_call
父進(jìn)程等待子進(jìn)程執(zhí)行命令癌椿,返回執(zhí)行命令的狀態(tài)碼健蕊,如果出現(xiàn)錯(cuò)誤,進(jìn)行報(bào)錯(cuò)踢俄。如果returncode不為0缩功,則舉出錯(cuò)誤subprocess.CalledProcessError,該對(duì)象包含有returncode屬性都办,可用try…except…來檢查嫡锌。
示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import subprocess
print "2. ################## subprocess.check_call ##########"
print u"check_call與call命令相同,區(qū)別是如果出錯(cuò)會(huì)報(bào)錯(cuò)"
subprocess.check_call(['dir'],shell=True)
subprocess.check_call(['abc'],shell=True)
print u"call方法與check_call方法都知識(shí)執(zhí)行并打印命令到輸出終端琳钉,但是
獲取不到势木,如果想獲取到結(jié)果使用check_output"
1.3 check_output
父進(jìn)程等待子進(jìn)程執(zhí)行命令,返回子進(jìn)程向標(biāo)準(zhǔn)輸出發(fā)送輸出運(yùn)行結(jié)果歌懒,檢查退出信息啦桌,如果returncode不為0,則舉出錯(cuò)誤subprocess.CalledProcessError及皂,該對(duì)象包含有returncode屬性和output屬性甫男,output屬性為標(biāo)準(zhǔn)輸出的輸出結(jié)果,可用try…except…來檢查验烧。
示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import subprocess
print "3. ################## subprocess.check_output ##############"
res1 = subprocess.call(['dir'],shell=True)
res2 = subprocess.check_call(['dir'],shell=True)
res3 = subprocess.check_output(['dir'],shell=True)
print u"call結(jié)果:",res1
print u"check_call結(jié)果:",res2
print u"check_output結(jié)果:\n",res3
可見板驳,call/check_call 返回值均是命令的執(zhí)行狀態(tài)碼,而check_output返回值是命令的執(zhí)行結(jié)果碍拆。
如果在執(zhí)行相關(guān)命令時(shí)若治,命令后帶有參數(shù)慨蓝,將程序名(即命令)和所帶的參數(shù)一起放在一個(gè)列表中傳遞給相關(guān)方法即可,例如:
import subprocess
retcode = subprocess.call(["ls", "-l"])
print retcode
1.4 Popen
實(shí)際上端幼,subprocess模塊中只定義了一個(gè)類: Popen礼烈。上面的幾個(gè)函數(shù)都是基于Popen()的封裝(wrapper)。從Python2.4開始使用Popen來創(chuàng)建進(jìn)程静暂,用于連接到子進(jìn)程的標(biāo)準(zhǔn)輸入/輸出/錯(cuò)誤中去济丘,還可以得到子進(jìn)程的返回值。這些封裝的目的在于讓我們?nèi)菀资褂米舆M(jìn)程洽蛀。當(dāng)我們想要更個(gè)性化我們的需求的時(shí)候,就要轉(zhuǎn)向Popen類疟赊,該類生成的對(duì)象用來代表子進(jìn)程郊供。
構(gòu)造函數(shù)如下:
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)
- 參數(shù)args可以是字符串或者序列類型(如:list,元組)近哟,用于指定進(jìn)程的可執(zhí)行文件及其參數(shù)驮审。如果是序列類型,第一個(gè)元素通常是可執(zhí)行文件的路徑吉执。我們也可以顯式的使用executeable參數(shù)來指定可執(zhí)行文件的路徑疯淫。
- 參數(shù)stdin, stdout, stderr分別表示程序的標(biāo)準(zhǔn)輸入、輸出戳玫、錯(cuò)誤句柄熙掺。他們可以是PIPE,文件描述符或文件對(duì)象咕宿,也可以設(shè)置為None币绩,表示從父進(jìn)程繼承。
- 如果參數(shù)shell設(shè)為true府阀,程序?qū)⑼ㄟ^shell來執(zhí)行缆镣。
- 參數(shù)env是字典類型,用于指定子進(jìn)程的環(huán)境變量试浙。如果env = None董瞻,子進(jìn)程的環(huán)境變量將從父進(jìn)程中繼承。
與上面的封裝不同田巴,Popen對(duì)象創(chuàng)建后钠糊,主程序不會(huì)自動(dòng)等待子進(jìn)程完成。我們必須調(diào)用對(duì)象的wait()方法固额,父進(jìn)程才會(huì)等待 (也就是阻塞block)眠蚂。
示例
#!/usr/bin/env python
import subprocess
child = subprocess.Popen(['ping','-c','4','www.baidu.com']) #創(chuàng)建一個(gè)子進(jìn) 程,進(jìn)程名為child斗躏,執(zhí)行操作ping -c 4 www.baidu.com
child.wait() #子進(jìn)程等待
print 'hello'
此外也可以在父進(jìn)程中對(duì)子進(jìn)程進(jìn)行其它操作逝慧。
child.poll() # 用于檢查子進(jìn)程是否已經(jīng)結(jié)束昔脯。設(shè)置并返回returncode屬性。
child.wait() # 等待子進(jìn)程結(jié)束笛臣。設(shè)置并返回returncode屬性云稚。
Popen.communicate(input=None)
# 與子進(jìn)程進(jìn)行交互。向stdin發(fā)送數(shù) 據(jù)沈堡,或從stdout和stderr中讀取數(shù)
據(jù)静陈。可選參數(shù)input指定發(fā)送到子進(jìn)程的參數(shù)诞丽。Communicate()返回一個(gè)元
組:(stdoutdata, stderrdata)鲸拥。注意:如果希望通過進(jìn)程的stdin向其發(fā)送
數(shù)據(jù),在創(chuàng)建Popen對(duì)象的時(shí)候僧免,參數(shù) stdin必須被設(shè)置為PIPE刑赶。同樣,
如果希望從stdout和stderr獲取數(shù)據(jù)懂衩,必 須將stdout和stderr設(shè)置為PIPE撞叨。
child.kill() # 終止子進(jìn)程
child.send_signal() # 向子進(jìn)程發(fā)送信號(hào)
child.terminate() # 終止子進(jìn)程
child.pid # 獲取子進(jìn)程的進(jìn)程ID。
child.returncode # 獲取進(jìn)程的返回值浊洞。如果進(jìn)程還沒有結(jié)束牵敷,返回None。
子進(jìn)程文本流控制
子進(jìn)程的標(biāo)準(zhǔn)輸入法希、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤如下屬性分別表示:
child.stdin | child.stdout | child.stderr
我們還可以在Popen()建立子進(jìn)程的時(shí)候改變標(biāo)準(zhǔn)輸入枷餐、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤,并可以利用subprocess.PIPE將多個(gè)子進(jìn)程的輸入和輸出連接在一起铁材,構(gòu)成管道(pipe)尖淘,如下2個(gè)例子:
例1
#!/usr/bin/env python
import subprocess
child = subprocess.Popen(['ls','-l'],stdout=subprocess.PIPE) #將標(biāo)準(zhǔn)輸出定向輸出到subprocess.PIPE
print child.stdout.read() #使用 child.communicate() 也可
例2
#!/usr/bin/env python
import subprocess
child1 = subprocess.Popen(['cat','/etc/passwd'],stdout=subprocess.PIPE)
child2 = subprocess.Popen(['grep','root'],stdin=child1.stdout,stdout=subprocess.PIPE)
print child2.communicate()
subprocess.PIPE實(shí)際上為文本流提供一個(gè)緩存區(qū)著觉。child1的stdout將文本輸出到緩存區(qū)村生,隨后child2的stdin從該P(yáng)IPE中將文本讀取走。child2的輸出文本也被存放在PIPE中饼丘,直到communicate()方法從PIPE中讀取出PIPE中的文本趁桃。
注意:communicate()是Popen對(duì)象的一個(gè)方法,該方法會(huì)阻塞父進(jìn)程肄鸽,直到子進(jìn)程完成
1.5 進(jìn)程通信
如果想得到進(jìn)程的輸出卫病,管道是個(gè)很方便的方法。
- 簡(jiǎn)單情況
例1
p=subprocess.Popen("dir", shell=True)
p.wait()
subprocess.call(["ls", "-l"])
subprocess.call("exit 1", shell=True)
shell參數(shù)根據(jù)你要執(zhí)行的命令的情況來決定典徘,上面是dir命令蟀苛,就一定要shell=True了,p.wait()可以得到命令的返回值逮诲。
如果上面寫成a=p.wait()帜平,a就是returncode幽告。那么輸出a的話,有可能就是0【表示執(zhí)行成功】裆甩。
- 分開輸出
例2
p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutput,erroutput) = p.communicate()
p.communicate會(huì)一直等到進(jìn)程退出冗锁,并將標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出返回,這樣就可以得到子進(jìn)程的輸出了嗤栓。
- 合并輸出
例3
p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
(stdoutput,erroutput) = p.communicate()
標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出是分開的冻河,也可以合并起來,只需要將stderr參數(shù)設(shè)置為subprocess.STDOUT就可以了茉帅。
- 逐行輸出
例4
p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
buff = p.stdout.readline()
if buff == '' and p.poll() != None:
break
- 死鎖
如果使用了管道叨叙,而又不去處理管道的輸出,如果子進(jìn)程輸出數(shù)據(jù)過多担敌,死鎖就會(huì)發(fā)生了摔敛。
例5
p=subprocess.Popen("longprint", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p.wait()
longprint是一個(gè)假想的有大量輸出的進(jìn)程,假設(shè)在xp, Python2.5的環(huán)境下全封,當(dāng)輸出達(dá)到4096時(shí),死鎖就發(fā)生了桃犬。當(dāng)然刹悴,如果我們用p.stdout.readline或者p.communicate去清理輸出,那么無論輸出多少攒暇,死鎖都是不會(huì)發(fā)生的土匀。或者我們不使用管道形用,比如不做重定向就轧,或者重定向到文件,也都是可以避免死鎖的田度。
- 多命令執(zhí)行
在shell中我們知道妒御,想要連接多個(gè)命令可以使用管道。
在subprocess中镇饺,可以使用上一個(gè)命令執(zhí)行的輸出結(jié)果作為下一次執(zhí)行的輸入乎莉。