subprocess--子進程管理器
一、subprocess 模塊簡介
subprocess最早是在2.4版本中引入的。
subprocess模塊用來生成子進程,並可以通過管道連接它們的輸入/輸出/錯誤,以及獲得它們的返回值。
它用來代替多個舊模塊和函數:
os.system
os.spawn*
os.popen*
popen2.*
commands.*
關於這個模塊可以取代的舊函數可以參見 subprocess-replacements 一節。
POSIX用戶(Linux, BSD, etc)還可以安裝和使用更新的subprocess32模塊來代替python 2.7版本中的subprocess.
subprocess32雖然是一個低版本,但在有些情況下效果更好。
1.1. 使用 subprocess模塊
啟動子進程的推薦方式是使用下面的便利功能。
當這些還不能滿足需求時,就需要使用底層的Popen接口。
1. subprocess.call
語法:
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允許我們運行這樣一些命令。
2. subprocess.check_call
語法:
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.
3. subprocess.check_output
語法:
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.
4. subprocess.PIPE
使用Popen時,用於 stdin, stdout和stderr參數的特殊值,表示打開連接標准流的管道。
5. subprocess.STDOUT
使用Popen時,用於 stderr 參數的特殊值,表示將標准錯誤重定向到標准輸出的同一個句柄。
6. 異常 subprocess.CalledProcessError
當由 check_call()或 check_output()運行的進程返回非零狀態值時拋出的異常。
7. returncode
子進程的退出狀態。
8. cmd
子進程執行的命令。
9. output
如果check_output()拋出異常時,子進程的輸出值。
否則,沒有這個值。
1.1.1. 常用的參數
為了支持各種用戶使用情況 ,Popen構建函數接收多種可選參數。
對於最典型的情況,許多參數都保留有安全的默認值,這些最常用的方式如下:
1. args
所有的函數都需要這個參數,並且它是一個字符串,或者是程序的參數序列。
提供一個參數序列是更推薦的方式,因為這樣能允許模塊接收空格 或 引號中的參數。
如果傳遞的是單個字符串,要么 shell=True, 或都要么 字符串就程序名字,並且不能帶參數。
2. stdin, stdout 和 stderr
stdin, stdout和stderr指定了執行程序的標准輸入,標准輸出和標准錯誤的文件句柄。
它們的值可以是PIPE, 一個存在的文件描述符(正整數),一個存在的文件對象,或 None.
PIPE 表示創建一個連接子進程的新管道。
默認值 為 None, 表示不做重定向。
子進程的文件句柄可以從父進程中繼承得到。
另外,stderr可以設置值為 STDOUT,表示子進程的錯誤數據可以和標准輸出是同一個文件句柄。
當stdout 或 stderr的值為管道 並且 universal_newlines的值為真時,
對於以 ‘U'模式參數打開的新行,所有行的結束都會轉換成'\n'。
3. shell
如果 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的字符等。
1.1.2. Popen構建函數
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(command_line)
>>> print args
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.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中實現。
1.1.3.異常
在開始執行新程序之前,子進程拋出的異常,會被重新拋出到父進程。
另外,異常對象會有一個額外的屬性,叫做 child_traceback, 它是一個字符串,包含從子程序的觀察點追蹤到的信息。
最常見的拋出的異常是 OSError, 當它發生時,通常是我們執行了一個不存在的文件。應用程序應當要能處理這個異常。
如果使用無效的參數調用 Popen,會拋出 ValueError異常。
如果被調用進程的返回碼不為零,則check_call()和check_output()會拋出 CalledProcessError異常。
1.1.4. 安全
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.
from:http://blog.chinaunix.net/uid-26000296-id-4461522.html