目錄html
subprocess--子進程管理器python
Top程序員
subprocess最先是在2.4版本中引入的。
subprocess模塊用來生成子進程,並能夠經過管道鏈接它們的輸入/輸出/錯誤,以及得到它們的返回值。
它用來代替多箇舊模塊和函數:
os.system
os.spawn*
os.popen*
popen2.*
commands.*
關於這個模塊能夠取代的舊函數能夠參見 subprocess-replacements 一節。
POSIX用戶(Linux, BSD, etc)還能夠安裝和使用更新的subprocess32模塊來代替python 2.7版本中的subprocess.
subprocess32雖然是一個低版本,但在有些狀況下效果更好。shell
啓動子進程的推薦方式是使用下面的便利功能。
當這些還不能知足需求時,就須要使用底層的Popen接口。緩存
語法:
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
語義:
運行由args指定的命令,直到命令結束後,返回 返回碼的屬性值。
上面的參數是最多見的方式,下面是示例代碼:
>>>
>>> subprocess.call(["ls", "-l"])
0
>>> subprocess.call("exit 1", shell=True)
1
WARNING: 使用 shell=True 是一種安全保護機制。
NOTE: 在使用這個函數時,不要使用 stdout=PIPE 或 stderr=PIPE 參數,
否則會致使子進程輸出的死鎖。
若是要使用管道,能夠在 communicate()方法中使用Popen
示例代碼:
import subprocess
rc = subprocess.call(["ls","-l"])
能夠經過一個shell來解釋一整個字符串:
import subprocess
out = subprocess.call("ls -l", shell=True)
out = subprocess.call("cd ..", shell=True)
使用了shell=True這個參數。
這個時候,咱們使用一整個字符串,而不是一個表來運行子進程。
Python將先運行一個shell,再用這個shell來解釋這整個字符串。
shell命令中有一些是shell的內建命令,這些命令必須經過shell運行,$cd。
shell=True容許咱們運行這樣一些命令。安全
語法:
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
語義:
運行由args指定的命令,直到命令執行完成。
若是返回碼爲零,則返回。不然,拋出 CalledProcessError異常。
CalledProcessError對象包含有返回碼的屬性值。
上面顯示的參數僅僅是最多見的,下面是用戶更經常使用的參數。
示例代碼以下:
>>>
>>> subprocess.check_call(["ls", "-l"])
0
>>> subprocess.check_call("exit 1", shell=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
這個函數在python 2.5版本中引入。
WARNING: 使用 shell=True 是一種安全機制。
NOTE: 不要在這個函數中使用 stdout=PIPE 或 stderr=PIPE, 不然會形成子進程死鎖。
若是須要使用管道,能夠在 communicate()方法中使用Popen.app
語法:
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)
語義:
運行args定義的命令,並返回一個字符串表示的輸出值。
若是返回碼爲非零,則拋出 CalledProcessError異常。
示例代碼:
>>>
>>> subprocess.check_output(["echo", "Hello World!"])
'Hello World!\n'
>>> subprocess.check_output("exit 1", shell=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
若是要捕捉結果中的標準錯誤,使用 stderr=subprocess.STDOUT參數:
>>>
>>> subprocess.check_output(
... "ls non_existent_file; exit 0",
... stderr=subprocess.STDOUT,
... shell=True)
'ls: non_existent_file: No such file or directory\n'
這個函數在python 2.7版本中引入。
WARNING: 使用 shell=True 是一種安全機制。
NOTE: 不要在這個函數中使用 stdout=PIPE 或 stderr=PIPE, 不然會形成子進程死鎖。
若是須要使用管道,能夠在 communicate()方法中使用Popen.函數
使用Popen時,用於 stdin, stdout和stderr參數的特殊值,表示打開鏈接標準流的管道。性能
使用Popen時,用於 stderr 參數的特殊值,表示將標準錯誤重定向到標準輸出的同一個句柄。this
當由 check_call()或 check_output()運行的進程返回非零狀態值時拋出的異常。
子進程的退出狀態。
子進程執行的命令。
若是check_output()拋出異常時,子進程的輸出值。
不然,沒有這個值。
爲了支持各類用戶使用狀況 ,Popen構建函數接收多種可選參數。
對於最典型的狀況,許多參數都保留有安全的默認值,這些最經常使用的方式以下:
全部的函數都須要這個參數,而且它是一個字符串,或者是程序的參數序列。
提供一個參數序列是更推薦的方式,由於這樣能容許模塊接收空格 或 引號中的參數。
若是傳遞的是單個字符串,要麼 shell=True, 或都要麼 字符串就程序名字,而且不能帶參數。
stdin, stdout和stderr指定了執行程序的標準輸入,標準輸出和標準錯誤的文件句柄。
它們的值能夠是PIPE, 一個存在的文件描述符(正整數),一個存在的文件對象,或 None.
PIPE 表示建立一個鏈接子進程的新管道。
默認值 爲 None, 表示不作重定向。
子進程的文件句柄能夠從父進程中繼承獲得。
另外,stderr能夠設置值爲 STDOUT,表示子進程的錯誤數據能夠和標準輸出是同一個文件句柄。
當stdout 或 stderr的值爲管道 而且 universal_newlines的值爲真時,
對於以 ‘U'模式參數打開的新行,全部行的結束都會轉換成'\n'。
若是 shell的值爲 True, 則指定的命令行會經過shell來執行。
若是你使用Python來做爲流程控制,那這樣的設置會頗有用,由於它提供了絕大多數的系統shell命令且能夠很方便地使用
shell的各類功能,如 shell 管道,文件名通配符,環境變量擴展,以及用戶目錄擴展符 ~。
可是,須要注意的是,Python 提供了相似shell功能的實現。
WARNING: 執行不受信任來源的shell命令會是一個嚴重的安全問題。
基於這一點,shell=True 是不建議的。
示例代碼以下:
>>>
>>> from subprocess import call
>>> filename = input("What file would you like to display?\n")
What file would you like to display?
non_existent; rm -rf / #
>>> call("cat " + filename, shell=True) # Uh-oh. This will end badly...
shell=False 關閉了shell的全部基本功能 ,從而不會有上面所說的安全漏洞。
能夠在Popen構建函數的幫助文檔中看到,它只有在 shell=False時才能工做。
當使用 shell=True時,pipes.quote()能夠被用於轉譯空格,shell的字符等。
subprocess中更底層的進程建立和管理能夠經過Popen類實現。
它提供了更多的靈活性,程序員經過它能處理更多複雜的狀況。
語法:
class 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)
語義:
在新進程中執行一個子程序。
在Unix中,這個類使用 相似於 os.execvp()方式來執行子程序。
在Windows中,這個類使用Windows的 CreateProcess()函數來執行子程序。
參數解析:
args: 一個程序參數序列,或者單個字符串。
默認的,要執行的程序應該是序列的第一個字段。
若是單個字符串,它的解析依賴於平臺
在Unix中,若是 args是一個字符串,那麼這個字符串解釋成被執行程序的名字或路徑。
然而,這種狀況只能用在不須要參數的程序。
NOTE: 當對args肯定了正確的分隔符後,shlex.split()就頗有用,特別是在複雜的狀況下:
>>>
>>> import shlex, subprocess
>>> command_line = raw_input()
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo 'MONEY′">>>args=shlex.split(commandline)>>>printargs[′/bin/vikings′,′−input′,′eggs.txt′,′−output′,′spamspam.txt′,′−cmd′,"echo′MONEY′">>>args=shlex.split(commandline)>>>printargs[′/bin/vikings′,′−input′,′eggs.txt′,′−output′,′spamspam.txt′,′−cmd′,"echo′MONEY'"]
>>> p = subprocess.Popen(args) # Success!
NOTE: 選項(如 -input) 和 參數(如 eggs.txt) 在shell中是用空格分隔成分離的列表元素。
若是參數須要引號或反斜線,則它們會是一個單一列表元素。
shell參數(默認值爲False)聲明瞭是否使用shell來執行程序。
若是 shell=True, 它將args看做是一個字符串,而不是一個序列。
在Unix系統,且 shell=True時,shell默認使用 /bin/sh.
若是 args是一個字符串,則它聲明瞭經過shell執行的命令。這意味着,字符串必需要使用正確的格式。
若是 args是一個序列,則第一個元素就是命令字符串,而其它的元素都做爲參數使用。
能夠這樣說,Popen等價於:
Popen(['/bin/sh', '-c', args[0], args[1], ...])
bufsize: 若是指定了值,則它和內建函數 open()對應的參數有相同的意義:
0 -- 表示不緩衝
1 -- 表示緩衝
任何其它的正數值表示buffer的大小。
負數值表示使用系統默認值,一般表示徹底緩衝。
它的默認值爲零。
NOTE: 若是遇到性能問題,建議將bufsize設置成 -1 或足夠大的正數(如 4096)。
executable: 指定了用於代替執行的程序。它極少會用到。
stdin, stdout, stderr:指定了執行程序的標準輸入,標準輸出和標準錯誤的文件句柄。
有效的值能夠是 PIPE, 一個存在的文件描述符,或存在的文件對象,或 None.
默認值爲 None。
stderr能夠設置成STDOUT, 它表示將子進程的stderr數據重定向到stdout.
preexec_fn: 若是它被設置成可調用對象,那麼這個對象會在子進程執行前被子進程調用,只用於Unix.
close_fds: 若是設置爲True, 則在子進程被執行前,除0,1和2以外的全部文件描述符都將被關閉,只用於Unix。
cwd: 當它不爲 None時,子程序在執行前,它的當前路徑會被替換成 cwd的值。
這個路徑並不會被添加到可執行程序的搜索路徑,因此cwd不能是相對路徑。
env: 當它不爲 None時,它是新進程的環境變量的映射。
能夠用它來代替當前進程的環境。
universal_newlines: 爲真時,文件對象 stdout和 stderr都被以文本文件的方式打開
示例代碼:
1. Popen對象建立後,主程序不會自動等待子進程完成。
咱們必須調用對象的wait()方法,父進程纔會等待 (也就是阻塞block):
import subprocess
child = subprocess.Popen(["ping","-c","5","www.google.com"])
print("parent process")
從運行結果中看到,父進程在開啓子進程以後並無等待child的完成,而是直接運行print。
2. 對比等待的狀況:
import subprocess
child = subprocess.Popen(["ping","-c","5","www.google.com"])
child.wait()
print("parent process")
此外,你還能夠在父進程中對子進程進行其它操做,好比咱們上面例子中的child對象:
child.poll() # 檢查子進程狀態
child.kill() # 終止子進程
child.send_signal() # 向子進程發送信號
child.terminate() # 終止子進程
子進程的PID存儲在child.pid
3. 能夠在Popen()創建子進程的時候改變標準輸入、標準輸出和標準錯誤,
並能夠利用subprocess.PIPE將多個子進程的輸入和輸出鏈接在一塊兒,構成管道(pipe):
import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()
print(out)
subprocess.PIPE實際上爲文本流提供一個緩存區。
child1的stdout將文本輸出到緩存區,隨後child2的stdin從該PIPE中將文本讀取走。
child2的輸出文本也被存放在PIPE中,直到communicate()方法從PIPE中讀取出PIPE中的文本。
要注意的是,communicate()是Popen對象的一個方法,該方法會阻塞父進程,直到子進程完成。
4. 還能夠利用communicate()方法來使用PIPE給子進程輸入:
import subprocess
child = subprocess.Popen(["cat"], stdin=subprocess.PIPE)
child.communicate("vamei")
咱們啓動子進程以後,cat會等待輸入,直到咱們用communicate()輸入"vamei"。
經過使用subprocess包,咱們能夠運行外部程序。這極大的拓展了Python的功能。
若是你已經瞭解了操做系統的某些應用,你能夠從Python中直接調用該應用(而不是徹底依賴Python),
並將應用的結果輸出給Python,並讓Python繼續處理。
shell的功能(好比利用文本流鏈接各個應用),就能夠在Python中實現。
在開始執行新程序以前,子進程拋出的異常,會被從新拋出到父進程。
另外,異常對象會有一個額外的屬性,叫作 child_traceback, 它是一個字符串,包含從子程序的觀察點追蹤到的信息。
最多見的拋出的異常是 OSError, 當它發生時,一般是咱們執行了一個不存在的文件。應用程序應當要能處理這個異常。
若是使用無效的參數調用 Popen,會拋出 ValueError異常。
若是被調用進程的返回碼不爲零,則check_call()和check_output()會拋出 CalledProcessError異常。
Unlike some other popen functions, this implementation will never call a system shell implicitly. This means that all characters, including shell metacharacters, can safely be passed to child processes. Obviously, if the shell is invoked explicitly, then it is the application’s responsibility to ensure that all whitespace and metacharacters are quoted appropriately.