python中的subprocess
- 2020 年 1 月 7 日
- 筆記
python2.7 源碼中的注釋(由於能力有限,翻譯的不太準確):
這個模組允許您開啟進程、連接輸入、輸出和錯誤的管道,並獲取他們的返回程式碼。這個模組計劃替代一些舊程式碼,如:
os.system、os.spawn*、os.Popen、popen2.* 、commands.*
關於subprocess模組可以用來取代這些模組和功能在下面可以找到
這個模組定義了一個Popen的類:
class 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):
參數為:
args應該是一個字元串或序列的程式命令及參數。程式通常執行序列或字元串的第一項,但可以通過使用明確的參數進行設置。
在UNIX上,shell = False(默認):在這種情況下,Popen類使用os.execvp()來執行程式的子進程。args應該通常是一個序列。字元串將被視為只有一個字元串的序列(程式執行)。
在UNIX上,shell= True:如果參數是一個字元串,它指定了通過shell執行命令字元串。如果參數是一個序列,第一項指定命令字元串,其他的將被視為附加的shell命令的參數。
在Windows:Popen類通過使用CreateProcess()執行這個子進程來對字元串操作。如果參數是一個序列,它將用list2cmdline方法將其轉換成一個字元串。請注意,並不是所有的MS Windows應用程式解釋命令行用相同的方法:list2cmdline是專為應用程式與MS C使用相同的規則。
bufsize,如果給定了,與內置行數open()的參數有相同意義:0意味著無緩衝的,1意味著線性緩衝,其他任何正值意味著使用的緩衝區(大約)大小。一個負bufsize意味著使用這個系統默認情況下,這通常意味著完全緩衝。默認值為bufsize是0(無緩衝的)。
stdin、stdout和stderr分別指定執行程式的標準輸入,標準輸出和標準錯誤。有效值是PIPE,現有的文件描述符(正整數),現有文件對象,None。PIPE創建一個新的子管道。None,沒有重定向;子管道將會繼承父管道的文件句柄。此外,標準錯誤可以用STDOUT來定義,表明應用程式應該從STDOUT捕獲到相同的文件句柄的標準錯誤數據。
如果preexec_fn設置為一個可調用對象,該對象將在子進程執行之前調用。
如果close_fds 為True,所有的文件描述符除了0、1和2都會在子進程執行之前關閉。
如果shell是True,將通過shell執行指定的命令。
如果 cwd 不為None,子進程將在執行前切換到 cwd指定的目錄
如果 env 不為空,為新進程定義環境變數
如果 universal_newlines 為 True, 則文件對象標準輸出、標準錯誤輸出以文本文件形式打開, 但是在unix系統中會以n結束,windows中以rn結束。 在python程式中都是看作為n 注意: 這種功能僅僅支援用通用換行符構建的python(默認)。同時文件對象標準輸出、標準輸入、標準錯誤的換行符屬性,不會被communicate()模組所更新。
如果給定了startupinfo and creationflags參數, 將會轉交給底層CreateProcess() 函數,他們可以指定諸如主窗體的外觀、新進程的優先順序之類的屬性(僅支援windows)
這個模組也定義了一些簡短的函數:
call(*popenargs, **kwargs):
運行帶參數的命令. 等待命令完成後返回返回碼屬性。
這些參數相對於Popen構造函數是相同的。
Example:
retcode = call(["ls", "-l"])
check_call(*popenargs, **kwargs):
運行帶參數的命令. 等待命令完成.如果退出碼是0則返回,如果是其他則拋出
CalledProcessError錯誤,該CalledProcessError 對象就會有返回返回碼屬性
這些參數相對於Popen構造函數是相同的。
Example:
check_call(["ls", "-l"])
check_output(*popenargs, **kwargs):
運行帶參數的命令並且以位元組字元串來返回。
如果退出碼是非0則會拋出CalledProcessError。
CalledProcessError的對象將有返回程式碼在returncode屬性和輸出在output屬性
這些參數相對於Popen構造函數是相同的。
Example:
output = check_output(["ls", "-l", "/dev/null"])
異常處理:
==============
在新程式開始執行之前子進程拋出異常,之後父進程重新拋出異常。此外,異常對象會有一個額外稱為'child_traceback'的屬性,從子進程的角度上看,這是一個包含錯誤資訊的字元串。
最常見的異常是OSError,比如:執行一個不存在的文件,應用程式會拋出OSError異常
如果Popen被無效的參數調用就會拋出『ValueError』
如果check_call() and check_output()在被調用過程中返回一個非零的返回碼則會拋出『CalledProcessError』
安全
==============
和其他popen函數不同,它不會隱式的執行/bin/sh,這意味著所有的字元,包括shell元字元,可以安全地傳遞給子進程。
Popen 對象
=============
Popen類的實例有以下方法
poll()
檢查子進程是否終止,返回returncode屬性
wait()
等待子進程終止。返回returncode屬性。
communicate(input=None)
與進程相互作用: 發送數據到標準輸入。從標準輸出、標準錯誤讀取數據, 直到到達文件尾。等待進程終止。可選的input參數應該是發送給子進程的字元串,或者如果沒有要發送給子進程的數據那就用None
communicate() 返回一個元組 (stdout, stderr).
注意:讀取的數據是保存在緩衝區中,因此,如果數據太大或沒有限制則不要使用這個方法
下面的屬性也是有效的:
=====================
stdin
如果stdin參數是PIPE,這個屬性是提供輸入到子進程一個文件對象,否則為None
stdout
如果stdout參數是PIPE , 這個屬性是提供輸出到子進程一個文件對象,否則為None
stderr
如果stderr參數是PIPE , 這個屬性是提供錯誤輸出到子進程一個文件對象,否則為None
pid
子進程的PID
returncode
子進程的返回碼。空值表示進程還沒有結束,一個負值『-N』表示子進程被訊號N所結束(僅unix支援)
用subprocess模組取代舊函數:
====================================================
在本節中, "a ==> b" 意味著 b 可以替代 a.
注意: 如果沒有找到執行程式,所有在本節中的函數都有可能以靜默狀態失敗;這個模組會拋出OSError異常
在以下的例子中, 我們假設subprocess 模組是"from subprocess import *" 這樣導入的:
替代 /bin/sh shell 的引號部分 --------------------------------- output=`mycmd myarg` ==> output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] 替代 shell 的管道 ------------------------- output=`dmesg | grep hda` ==> p1 = Popen(["dmesg"], stdout=PIPE) p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) output = p2.communicate()[0] 替代 os.system() --------------------- sts = os.system("mycmd" + " myarg") ==> p = Popen("mycmd" + " myarg", shell=True) pid, sts = os.waitpid(p.pid, 0) 注意: * 通過shell調用程式通常不是必須的 * 查看returncode attribute要比exitstatus容易些. 一個更現實的例子: try: retcode = call("mycmd" + " myarg", shell=True) if retcode < 0: print >>sys.stderr, "Child was terminated by signal", -retcode else: print >>sys.stderr, "Child returned", retcode except OSError, e: print >>sys.stderr, "Execution failed:", e 替代 os.spawn* ------------------- P_NOWAIT example: pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") ==> pid = Popen(["/bin/mycmd", "myarg"]).pid P_WAIT example: retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") ==> retcode = call(["/bin/mycmd", "myarg"]) Vector example: os.spawnvp(os.P_NOWAIT, path, args) ==> Popen([path] + args[1:]) Environment example: os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) ==> Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) 替代 os.popen* ------------------- pipe = os.popen("cmd", mode='r', bufsize) ==> pipe = Popen("cmd", shell=True, bufsize=bufsize, stdout=PIPE).stdout pipe = os.popen("cmd", mode='w', bufsize) ==> pipe = Popen("cmd", shell=True, bufsize=bufsize, stdin=PIPE).stdin (child_stdin, child_stdout) = os.popen2("cmd", mode, bufsize) ==> p = Popen("cmd", shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, close_fds=True) (child_stdin, child_stdout) = (p.stdin, p.stdout) (child_stdin, child_stdout, child_stderr) = os.popen3("cmd", mode, bufsize) ==> p = Popen("cmd", shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) (child_stdin, child_stdout, child_stderr) = (p.stdin, p.stdout, p.stderr) (child_stdin, child_stdout_and_stderr) = os.popen4("cmd", mode, bufsize) ==> p = Popen("cmd", shell=True, bufsize=bufsize, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) (child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) 在 Unix系統中, os.popen2, os.popen3 與 os.popen4 同樣可以在沒有shell介入的情況下直接傳遞給程式 以序列形式執行命令行 這種方法可以用下面的方法替換: (child_stdin, child_stdout) = os.popen2(["/bin/ls", "-l"], mode, bufsize) ==> p = Popen(["/bin/ls", "-l"], bufsize=bufsize, stdin=PIPE, stdout=PIPE) (child_stdin, child_stdout) = (p.stdin, p.stdout) Return code handling translates as follows: pipe = os.popen("cmd", 'w') ... rc = pipe.close() if rc is not None and rc % 256: print "There were some errors" ==> process = Popen("cmd", 'w', shell=True, stdin=PIPE) ... process.stdin.close() if process.wait() != 0: print "There were some errors" 替代 popen2.* ------------------ (child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) ==> p = Popen(["somestring"], shell=True, bufsize=bufsize stdin=PIPE, stdout=PIPE, close_fds=True) (child_stdout, child_stdin) = (p.stdout, p.stdin) 在 Unix系統中, popen2 也可以在沒有shell介入的情況下直接傳遞給程式以序列形式執行命令行. 這種方法可以用下面的方法替換: (child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) ==> p = Popen(["mycmd", "myarg"], bufsize=bufsize, stdin=PIPE, stdout=PIPE, close_fds=True) (child_stdout, child_stdin) = (p.stdout, p.stdin) The popen2.Popen3 and popen2.Popen4 basically works as subprocess.Popen, except that: * subprocess.Popen raises an exception if the execution fails * the capturestderr argument is replaced with the stderr argument. * stdin=PIPE and stdout=PIPE must be specified. * popen2 closes all filedescriptors by default, but you have to specify close_fds=True with subprocess.Popen.