Python中可以執(zhí)行shell命令的相關(guān)模塊和函數(shù)有:
os.system
os.spawn*
os.popen* --廢棄
popen2.* --廢棄
commands.* --廢棄,3.x中被移除
隨著Python版本的更新,過多的模塊引起代碼的復(fù)雜與冗余,因此Python新引入了一個模塊subprocess,將以上幾個模塊中的功能集中到它當(dāng)中,以后我們只需import這一個即可。
1、subprocess模塊
subprocess的目的就是啟動一個新的進程并且與之通信。
- 運行python的時候,我們都是在創(chuàng)建并運行一個進程。像Linux進程那樣,一個進程可以fork一個子進程,并讓這個子進程exec另外一個程序。在Python中,我們通過標(biāo)準(zhǔn)庫中的subprocess包來fork一個子進程,并運行一個外部的程序。
- subprocess包中定義有數(shù)個創(chuàng)建子進程的函數(shù),這些函數(shù)分別以不同的方式創(chuàng)建子進程,所以我們可以根據(jù)需要來從中選取一個使用。另外subprocess還提供了一些管理標(biāo)準(zhǔn)流(standard stream)和管道(pipe)的工具,從而在進程間使用文本通信。
1.1 call
父進程等待子進程執(zhí)行命令,返回子進程執(zhí)行命令的狀態(tài)碼(returncode,相當(dāng)于Linux exit code),如果出現(xiàn)錯誤,不進行報錯。
- 這里說的返回執(zhí)行命令的狀態(tài)碼的意思是:如果我們通過一個變量 res = subprocess.call(['dir',shell=True]) 獲取的執(zhí)行結(jié)果,我們能獲取到的是子進程執(zhí)行命令執(zhí)行結(jié)果的狀態(tài)碼,即res=0/1 執(zhí)行成功或者不成功,并不代表說看不到執(zhí)行結(jié)果,在Python的console界面中我們是能夠看到命令結(jié)果的,只是獲取不到。想獲取執(zhí)行的返回結(jié)果,請看check_output。
- 不進行報錯解釋:如果我們執(zhí)行的命令在執(zhí)行時,操作系統(tǒng)不識別,系統(tǒng)會返回一個錯誤,如:abc命令不存在,這個結(jié)果會在console界面中顯示出來,但是我們的Python解釋器不會提示任何信息,如果想讓Python解釋器也進行報錯,請看check_call
示例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import subprocess
print "1. ################## subprocess.call ###############"
print u"call方法調(diào)用系統(tǒng)命令進行執(zhí)行,如果出錯不報錯"
subprocess.call(['dir'],shell=True)
注:shell默認(rèn)為False,在Linux下,shell=False時, Popen調(diào)用os.execvp()執(zhí)行args指定的程序;shell=True時,如果args是字符串,Popen直接調(diào)用系統(tǒng)的Shell來執(zhí)行args指定的程序,如果args是一個序列,則args的第一項是定義程序命令字符串,其它項是調(diào)用系統(tǒng)Shell時的附加參數(shù)。
在Windows下,不論shell的值如何,Popen調(diào)用CreateProcess()執(zhí)行args指定的外部程序。如果args是一個序列,則先用list2cmdline()轉(zhuǎn)化為字符串,但需要注意的是,并不是MS Windows下所有的程序都可以用list2cmdline來轉(zhuǎn)化為命令行字符串。在windows下,調(diào)用腳本時要寫上shell=True。
1.2 check_call
父進程等待子進程執(zhí)行命令,返回執(zhí)行命令的狀態(tài)碼,如果出現(xiàn)錯誤,進行報錯。如果returncode不為0,則舉出錯誤subprocess.CalledProcessError,該對象包含有returncode屬性,可用try…except…來檢查。
示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import subprocess
print "2. ################## subprocess.check_call ##########"
print u"check_call與call命令相同,區(qū)別是如果出錯會報錯"
subprocess.check_call(['dir'],shell=True)
subprocess.check_call(['abc'],shell=True)
print u"call方法與check_call方法都知識執(zhí)行并打印命令到輸出終端,但是
獲取不到,如果想獲取到結(jié)果使用check_output"
1.3 check_output
父進程等待子進程執(zhí)行命令,返回子進程向標(biāo)準(zhǔn)輸出發(fā)送輸出運行結(jié)果,檢查退出信息,如果returncode不為0,則舉出錯誤subprocess.CalledProcessError,該對象包含有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ù)一起放在一個列表中傳遞給相關(guān)方法即可,例如:
import subprocess
retcode = subprocess.call(["ls", "-l"])
print retcode
1.4 Popen
實際上,subprocess模塊中只定義了一個類: Popen。上面的幾個函數(shù)都是基于Popen()的封裝(wrapper)。從Python2.4開始使用Popen來創(chuàng)建進程,用于連接到子進程的標(biāo)準(zhǔn)輸入/輸出/錯誤中去,還可以得到子進程的返回值。這些封裝的目的在于讓我們?nèi)菀资褂米舆M程。當(dāng)我們想要更個性化我們的需求的時候,就要轉(zhuǎn)向Popen類,該類生成的對象用來代表子進程。
構(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,元組),用于指定進程的可執(zhí)行文件及其參數(shù)。如果是序列類型,第一個元素通常是可執(zhí)行文件的路徑。我們也可以顯式的使用executeable參數(shù)來指定可執(zhí)行文件的路徑。
- 參數(shù)stdin, stdout, stderr分別表示程序的標(biāo)準(zhǔn)輸入、輸出、錯誤句柄。他們可以是PIPE,文件描述符或文件對象,也可以設(shè)置為None,表示從父進程繼承。
- 如果參數(shù)shell設(shè)為true,程序?qū)⑼ㄟ^shell來執(zhí)行。
- 參數(shù)env是字典類型,用于指定子進程的環(huán)境變量。如果env = None,子進程的環(huán)境變量將從父進程中繼承。
與上面的封裝不同,Popen對象創(chuàng)建后,主程序不會自動等待子進程完成。我們必須調(diào)用對象的wait()方法,父進程才會等待 (也就是阻塞block)。
示例
#!/usr/bin/env python
import subprocess
child = subprocess.Popen(['ping','-c','4','www.baidu.com']) #創(chuàng)建一個子進 程,進程名為child,執(zhí)行操作ping -c 4 www.baidu.com
child.wait() #子進程等待
print 'hello'
此外也可以在父進程中對子進程進行其它操作。
child.poll() # 用于檢查子進程是否已經(jīng)結(jié)束。設(shè)置并返回returncode屬性。
child.wait() # 等待子進程結(jié)束。設(shè)置并返回returncode屬性。
Popen.communicate(input=None)
# 與子進程進行交互。向stdin發(fā)送數(shù) 據(jù),或從stdout和stderr中讀取數(shù)
據(jù)??蛇x參數(shù)input指定發(fā)送到子進程的參數(shù)。Communicate()返回一個元
組:(stdoutdata, stderrdata)。注意:如果希望通過進程的stdin向其發(fā)送
數(shù)據(jù),在創(chuàng)建Popen對象的時候,參數(shù) stdin必須被設(shè)置為PIPE。同樣,
如果希望從stdout和stderr獲取數(shù)據(jù),必 須將stdout和stderr設(shè)置為PIPE。
child.kill() # 終止子進程
child.send_signal() # 向子進程發(fā)送信號
child.terminate() # 終止子進程
child.pid # 獲取子進程的進程ID。
child.returncode # 獲取進程的返回值。如果進程還沒有結(jié)束,返回None。
子進程文本流控制
子進程的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤如下屬性分別表示:
child.stdin | child.stdout | child.stderr
我們還可以在Popen()建立子進程的時候改變標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤,并可以利用subprocess.PIPE將多個子進程的輸入和輸出連接在一起,構(gòu)成管道(pipe),如下2個例子:
例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實際上為文本流提供一個緩存區(qū)。child1的stdout將文本輸出到緩存區(qū),隨后child2的stdin從該PIPE中將文本讀取走。child2的輸出文本也被存放在PIPE中,直到communicate()方法從PIPE中讀取出PIPE中的文本。
注意:communicate()是Popen對象的一個方法,該方法會阻塞父進程,直到子進程完成
1.5 進程通信
如果想得到進程的輸出,管道是個很方便的方法。
- 簡單情況
例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會一直等到進程退出,并將標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔ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)錯誤輸出是分開的,也可以合并起來,只需要將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
- 死鎖
如果使用了管道,而又不去處理管道的輸出,如果子進程輸出數(shù)據(jù)過多,死鎖就會發(fā)生了。
例5
p=subprocess.Popen("longprint", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p.wait()
longprint是一個假想的有大量輸出的進程,假設(shè)在xp, Python2.5的環(huán)境下,當(dāng)輸出達到4096時,死鎖就發(fā)生了。當(dāng)然,如果我們用p.stdout.readline或者p.communicate去清理輸出,那么無論輸出多少,死鎖都是不會發(fā)生的。或者我們不使用管道,比如不做重定向,或者重定向到文件,也都是可以避免死鎖的。
- 多命令執(zhí)行
在shell中我們知道,想要連接多個命令可以使用管道。
在subprocess中,可以使用上一個命令執(zhí)行的輸出結(jié)果作為下一次執(zhí)行的輸入。