調(diào)用外部命令/可執(zhí)行文件

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í)行的輸入乎莉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奸笤,隨后出現(xiàn)的幾起案子惋啃,更是在濱河造成了極大的恐慌,老刑警劉巖监右,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件边灭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡健盒,警方通過查閱死者的電腦和手機(jī)绒瘦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門称簿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人椭坚,你說我怎么就攤上這事予跌。” “怎么了善茎?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵券册,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我垂涯,道長(zhǎng)烁焙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任耕赘,我火速辦了婚禮骄蝇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘操骡。我一直安慰自己九火,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布册招。 她就那樣靜靜地躺著岔激,像睡著了一般。 火紅的嫁衣襯著肌膚如雪是掰。 梳的紋絲不亂的頭發(fā)上虑鼎,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音键痛,去河邊找鬼炫彩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛絮短,可吹牛的內(nèi)容都是我干的江兢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼戚丸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼划址!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起限府,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤夺颤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后胁勺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體世澜,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年署穗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寥裂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嵌洼。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖封恰,靈堂內(nèi)的尸體忽然破棺而出麻养,到底是詐尸還是另有隱情,我是刑警寧澤诺舔,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布鳖昌,位于F島的核電站,受9級(jí)特大地震影響低飒,放射性物質(zhì)發(fā)生泄漏许昨。R本人自食惡果不足惜梅割,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一棉姐、第九天 我趴在偏房一處隱蔽的房頂上張望该押。 院中可真熱鬧孽拷,春花似錦、人聲如沸派诬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)尿背。三九已至琅坡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間残家,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工售躁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坞淮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓陪捷,卻偏偏與公主長(zhǎng)得像回窘,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子市袖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 處于學(xué)習(xí)別人代碼風(fēng)格階段啡直,github參考學(xué)習(xí)程序程序開頭會(huì)有 一是用來指定腳本語(yǔ)言為 Python,二是用來指定...
    lifesmily閱讀 1,055評(píng)論 0 0
  • 1.os.system('cmd') os.system('cat /proc/cpuinfo') 直接執(zhí)行參數(shù)中...
    奇_66a0閱讀 14,139評(píng)論 0 5
  • From: https://blog.linuxeye.com/375.html 從Python 2.4開始苍碟,Py...
    pzka158閱讀 3,006評(píng)論 0 2
  • 其實(shí)應(yīng)該搞清楚何時(shí)要用python寫腳本酒觅,何時(shí)用shell例如對(duì)于系統(tǒng)操作的業(yè)務(wù),我傾向于用shell awk ...
    帥子鍋閱讀 12,947評(píng)論 0 6
  • 以下是丹麥國(guó)家旅游局關(guān)于“博恩霍姆島”的官方介紹: ================== 博恩霍姆島是波羅的海里的...
    MW_WM閱讀 1,257評(píng)論 0 1