1、介紹
subprocess模塊可以生成新的進程,連接到它們的input/output/error管道,同時獲取它們的返回碼。
2、基本操作方法
2.1、subprocess的run、call、check_call、check_output函數
subprocess.run(args[, stdout, stderr, shell ...])
- 執行args命令,返回值為CompletedProcess類;
- 若未指定stdout,則命令執行后的結果輸出到屏幕上,函數返回值CompletedProcess中包含有args和returncode;
- 若指定有stdout,則命令執行后的結果輸出到stdout中,函數返回值CompletedProcess中包含有args、returncode和stdout;
- 若執行成功,則returncode為0;若執行失敗,則returncode為1;
- 若想獲取args命令執行后的輸出結果,命令為:output = subprocess.run(args, stdout=subprocess.PIPE).stdout
subprocess.call(args[, stdout, ...])
- 執行args命令,返回值為命令執行狀態碼;
- 若未指定stdout,則命令執行后的結果輸出到屏幕;
- 若指定stdout,則命令執行后的結果輸出到stdout;
- 若執行成功,則函數返回值為0;若執行失敗,則函數返回值為1;
- (類似os.system)
subprocess.check_call(args[, stdout, ...])
- 執行args命令,返回值為命令執行狀態碼;
- 若未指定stdout,則命令執行后的結果輸出到屏幕;
- 若指定stdout,則命令執行后的結果輸出到stdout;
- 若執行成功,則函數返回值為0;若執行失敗,拋出異常;
- (類似subprocess.run(args, check=True))
subprocess.check_output(args[, stderr, ...])
- 執行args命令,返回值為命令執行的輸出結果;
- 若執行成功,則函數返回值為命令輸出結果;若執行失敗,則拋出異常;
- (類似subprocess.run(args, check=True, stdout=subprocess.PIPE).stdout)
其他
- args:啟動進程的參數,默認為字符串序列(列表或元組),也可為字符串(設為字符串時一般需將shell參數賦值為True);
- shell:shell為True,表示args命令通過shell執行,則可訪問shell的特性;
- check:check為True時,表示執行命令的進程以非0狀態碼退出時會拋出;subprocess.CalledProcessError異常;check為False時,狀態碼為非0退出時不會拋出異常;
- stdout、stdin、stderr:分別表示程序標準標輸出、輸入、錯誤信息;
- run函數返回值為CompletedProcess類,若需獲取執行結果,可通過獲取返回值的stdout和stderr來捕獲;
- check_output函數若需捕獲錯誤信息,可通過stderr=subprocess.STDOUT來獲取;
示例
# ################### subprocess.run 使用
def subprocess_run():result1 = subprocess.run(["adb", "devices"])print("result1:", result1)print("----------")result2 = subprocess.run("adb devices", shell=True, check=True)print("result2:", result2)print("----------")result3 = subprocess.run(["adb", "devices"], stdout=subprocess.PIPE)print("result3:", result3)print(type(result3))
subprocess_run()""" 輸出如下
List of devices attached
338b123f0504 deviceresult1: CompletedProcess(args=['adb', 'devices'], returncode=0)
----------
List of devices attached
338b123f0504 deviceresult2: CompletedProcess(args='adb devices', returncode=0)
----------
result3: CompletedProcess(args=['adb', 'devices'], returncode=0, stdout=b'List of devices attached \r\n338b123f0504\tdevice\r\n\r\n')
<class 'subprocess.CompletedProcess'>
"""# ################### subprocess.call使用
def subprocess_call():result1 = subprocess.call(["adb", "devices"])print("result1:", result1)print("----------")result2 = subprocess.call(["adb", "devices"], stdout=subprocess.PIPE)print("result2:", result2)
subprocess_call()"""結果
List of devices attached
338b123f0504 deviceresult1: 0
----------
result2: 0
"""# ################### subprocess.check_call
def subprocess_check_call():result1 = subprocess.check_call(["adb", "devices"])print("result1:", result1)print("----------")result2 = subprocess.check_call(["adb", "devices"], stdout=subprocess.PIPE)print("result2:", result2)
subprocess_check_call()"""結果
List of devices attached
338b123f0504 deviceresult1: 0
----------
result2: 0
"""# ################### subprocess.check_output
def subprocess_check_output():result1 = subprocess.check_output(["adb", "devices"])print("result1:", result1)print("----------")result2 = subprocess.run(["adb", "devices"], stdout=subprocess.PIPE).stdoutprint("result2:", result2)
subprocess_check_output()"""結果
result1: b'List of devices attached \r\n338b123f0504\tdevice\r\n\r\n'
----------
result2: b'List of devices attached \r\n338b123f0504\tdevice\r\n\r\n'
"""
2.2、subprocess的getoutput、getstatusoutput函數
subprocess.getoutput(cmd)
執行cmd命令,返回值為命令執行的輸出結果(字符串類型);執行失敗,不會拋出異常(類似os.popen(cmd).read());
cmd:參數,字符串類型;
subprocess.getstatusoutput(cmd)
執行cmd命令,返回值為元組類型(命令執行狀態, 命令執行的輸出結果);元組中命令執行狀態為0,表示執行成功;命令執行狀態為1,表示執行失敗;
cmd:參數,字符串類型;
示例
# subprocess.getoutput或getstatusoutput使用
def subprocess_get_output():result1 = subprocess.getoutput("adb devices")print("result1:", result1)print(type(result1))print("**** subprocess.getstatusoutput ****")result2 = subprocess.getstatusoutput("adb devices")print("result2:", result2)print(type(result2))
subprocess_get_output()
"""結果
**** subprocess.getoutput ****
result1: List of devices attached
338b123f0504 device<class 'str'>
**** subprocess.getstatusoutput ****
result2: (0, 'List of devices attached \n338b123f0504\tdevice\n')
<class 'tuple'>
"""
3、 subprocess.Popen類
subprocess.Popen類用于在一個新進程中執行一個子程序,上述subprocess函數均是基于subprocess.Popen類;
subprocess.Popen(args[, bufsize, stdin, stdout, stderr, ...]) 語法
Popen類的構造函數,返回結果為subprocess.Popen對象;
- args:需要執行的系統命令,可為字符串序列(列表或元組,shell為默認值False即可,建議為序列),也可為字符串(使用字符串時,需將shell賦值為True);
- shell:默認False,若args為序列時,shell=False;若args為字符串時,shell=True,表示通過shell執行命令;
- stdout、stdin、stderr:分別表示子程序標準輸出、標準輸入、標準錯誤,可為subprocess.PIPE、一個有效的文件描述符、文件對象或None。
若為subprocess.PIPE:代表打開通向標準流的管道,創建一個新的管道;
若為None:表示沒有任何重定向,子進程會繼承父進程;
stderr也可為subprocess.STDOUT:表示將子程序的標準錯誤輸出重定向到了標準輸出 - bufsize:默認0;指定緩沖策略,0表示不緩沖,1表示行緩沖,其它整數表示緩沖區大小,負數表示使用系統
- cwd:默認None;若非None,則表示將會在執行這個子進程之前改變當前工作目錄;
- env:用于指定子進程的環境變量。若env為None,那么子進程的環境變量將從父進程中繼承;若env非None,則表示子程序的環境變量由env值來設置,它的值必須是一個映射對象。
- universal_newlines: 不同系統的換行符不同。若True,則該文件對象的stdin,stdout和stderr將會以文本流方式打開;否則以二進制流方式打開
- preexec_fn:只在Unix平臺下有效,用于指定一個可執行對象(callable object),它將在子進程運行之前被調用。
- Close_sfs:在windows平臺下,如果close_fds被設置為True,則新創建的子進程將不會繼承父進程的輸入、輸出、錯誤管道。我們不能將close_fds設置為True同時重定向子進程的標準輸入、輸出與錯誤(stdin, stdout, stderr)。
- startupinfo與createionflags只在windows下有效,它們將被傳遞給底層的CreateProcess()函數,用于設置子進程的一些屬性,如:主窗口的外觀,進程的優先級等等。
subprocess.Popen對象常用方法
下列 PopenObject 為 subprocess.Popen 對象
- PopenObject.poll() :用于檢查命令是否已經執行結束,若結束返回狀態碼;若未結束返回None;
- PopenObject.wait([timeout, endtime]):等待子進程結束,并返回狀態碼;若超過timeout(s)進程仍未結束,則拋出異常;
- PopenObject.send_signal(signal):發送信號signal給子進程;
- PopenObject.terminate():停止子進程;在windows平臺下,該方法將調用Windows API TerminateProcess()來結束子進程。
- PopenObject.kill():殺死子進程;
- PopenObject.communicate([input, timeout]):與進程進行交互(如向stdin發送數據,或從stdout和stderr中讀取數據),它會阻塞父進程,直到子進程完成;注意:如果希望通過進程的stdin向其發送數據,在創建Popen對象的時候,參數stdin必須被設置為PIPE。同樣,如果希望從stdout和stderr獲取數據,必須將stdout和stderr設置為PIPE。
- input:表示將發送到子進程的字符串數據,默認為None;
- timeout:超時判斷,若超過timeout秒后仍未結束則拋出TimeoutExpired異常;
- communicate返回值:一個元組(stdout_data, stderr_data)
- 要注意的是,subprocess.PIPE實際上為文本流提供一個緩存區。直到communicate()方法從PIPE中讀取出PIPE中的文本.此方法比wait()好用。https://www.jianshu.com/p/8e582146bd4c
- PopenObject.pid:獲取子進程的進程ID。
- PopenObject.returncode:獲取進程的返回值。如果進程還沒有結束,返回None。
subprocess.Popen 對象的文本或字節流控制
-
PopenObject.stdin:
若PopenObject中stdin為PIPE,則返回一個可寫流對象;若encoding或errors參數被指定或universal_newlines參數為True,則此流是一個文件流,否則為字節流。
若PopenObject中stdin不是PIPE,則屬性為None。
stdin輸入流非None,可執行寫操作即PopenObject.stdin.write(s) -
PopenObject.stdout:
若PopenObject中stdout為PIPE,則返回一個可讀流對象;若encoding或errors參數被指定或universal_newlines參數為True,則此流是一個文件流,否則為字節流。
若PopenObject中stdout不是PIPE,則屬性為None。
stdout輸出流非None,可執行讀操作即PopenObject.stdout.read()或.readlines() -
PopenObject.stderr:
若PopenObject中stderr為PIPE,則返回一個可讀流對象;若encoding或errors參數被指定或universal_newlines參數為True,則此流是一個文件流,否則為字節流。
若PopenObject中stderr不是PIPE,則屬性為None。
stderr錯誤流非None,可執行讀操作即PopenObject.stderr.read()或.readlines()
def subprocess_Popen1():print("***通過communicate函數分別輸出PopenObject對象的輸出流和錯誤流***")args = [["adb", "devices"], ["adb", "devices11"]]for arg in args:popen_object = subprocess.Popen(arg, stdout=subprocess.PIPE, stderr=subprocess.PIPE)object_stdout, object_stderr = popen_object.communicate()output = {"popen_object": popen_object,"object_stdout": object_stdout,"object_stderr": object_stderr}print(output)"""{'popen_object': <subprocess.Popen object at 0x0000000002212400>, 'object_stdout': b'List of devices attached \r\n106D111805005938\tdevice\r\n\r\n', 'object_stderr': b''}{'popen_object': <subprocess.Popen object at 0x0000000002577C18>, 'object_stdout': b'', 'object_stderr': b'Android Debug Bridge version 1.0.31\r\n\r\n -a .....}"""print("***通過stdout和stderr方法輸出PopenObject對象輸出流和錯誤流***")p0 = subprocess.Popen(["adb", "devices"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)object_stdout = p0.stdout.read()p0.stdout.close()object_stderr = p0.stderr.read()p0.stderr.close()print(object_stdout) # 結果:b'List of devices attached \r\n338b123f0504\tdevice\r\n\r\n'print(object_stderr) # 結果:b''print("***Popen對象stdin寫入功能:使用stdout和stderr輸出")args = ["python", "python1"]for arg in args:p4 = subprocess.Popen([arg], shell=True, stdout=subprocess.PIPE,stdin=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)p4.stdin.write("print('hello')")p4.stdin.close()out = p4.stdout.read()p4.stdout.close()err = p4.stderr.read()p4.stderr.close()print("out:%s err:%s" % (out, err))"""***Popen對象stdin寫入功能out:helloerr:out: err:'python1' 不是內部或外部命令,也不是可運行的程序或批處理文件。"""print("***Popen對象stdin寫入功能:使用communicate輸出")p4 = subprocess.Popen(["python"], stdout=subprocess.PIPE,stdin=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)p4.stdin.write("print('hello')")output = p4.communicate()print(output) # 結果:('hello\n', '')print("***不含encoding參數***")p1 = subprocess.Popen("adb devices", shell=True, stdout=subprocess.PIPE)out1 = p1.stdout.readlines()print(out1) # 結果: [b'List of devices attached \r\n', b'106D111805005938\tdevice\r\n', b'\r\n']print("***含encoding參數***")p2 = subprocess.Popen("adb devices", shell=True, stdout=subprocess.PIPE, encoding="utf-8")out2 = p2.stdout.readlines()print(out2) # 結果: ['List of devices attached \n', '106D111805005938\tdevice\n', '\n']print("***Popen對象檢查命令是否結束,等待進程結束")print(p2.poll()) # 結果: Noneprint(p2.wait()) # 結果: 0print(p2.poll()) # 結果: 0print("***Popen對象communicate函數,它會阻塞父進程直至子進程完成")p3 = subprocess.Popen("adb devices", shell=True, stdout=subprocess.PIPE)out = p3.communicate()[0]print(out) # 結果:b'List of devices attached \r\n338b123f0504\tdevice\r\n\r\n'print(p3.poll()) # 結果:0
subprocess_Popen1()def subprocess_Popen2():"""1. 通過管道功能,實現adb shell ps | findstr top功能2. 直接為args賦值為一個字符串,實現adb shell ps | findstr top功能:return:"""print("***通過管道方式***")p1 = subprocess.Popen(["adb", "shell", "ps"], stdout=subprocess.PIPE)p2 = subprocess.Popen(["findstr", "top"], stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)out, err = p2.communicate()print(out, err) # 結果:b'shell 8508 8504 2600 1044 c004e5f8 b6f40938 S top\r\r\n' b''print("***通過傳一個字符串方式***")p3 = subprocess.Popen("adb shell ps | findstr top", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)out, err = p3.communicate()print(out, err) # 結果:b'shell 8508 8504 2600 1044 c004e5f8 b6f40938 S top\r\r\n' b''
subprocess_Popen2()
報錯解決
報錯信息
Traceback (most recent call last):File "C:\Users\fenglepeng\.conda\envs\py37\lib\threading.py", line 926, in _bootstrap_innerself.run()File "C:\Users\fenglepeng\.conda\envs\py37\lib\threading.py", line 870, in runself._target(*self._args, **self._kwargs)File "C:\Users\fenglepeng\.conda\envs\py37\lib\subprocess.py", line 1267, in _readerthreadbuffer.append(fh.read())
UnicodeDecodeError: 'gbk' codec can't decode byte 0xac in position 421: illegal multibyte sequence
解決
# 參數中增加 encoding='utf-8'
sub = subprocess.Popen(command, shell=True, cwd=cwd, universal_newlines=True,stdout=subprocess.PIPE, stdin=subprocess.PIPE,stderr=subprocess.PIPE, encoding='utf-8')
?