利用python subprocess模塊從系統獲取程序運行狀態返回值的方法

最近在編寫一個工具的時候遇到一個很棘手的問題:linux開發環境下對靜態語言程序進行自動化批量編譯,需要根據編譯結果執行一定的操作,但是bash調用編譯器的bin程序對文件進行編譯之後的狀態有點棘手。
一開始想到的方法是,直接在工具中寫死:

import os
os.system('gcc <_file> ;echo $?')

通過在執行編譯命令後緊接着執行echo $?命令獲取上一條命令gcc <_file>的狀態返回值,當編譯無誤時獲得標準輸出0,異常的時候同時標準輸出編譯失敗的標準錯誤輸出,以及echo獲取的狀態碼:

stanpao@vm:~$ gcc haha ;echo $?
/usr/bin/ld:haha: file format not recognized; treating as linker script
/usr/bin/ld:haha:1: syntax error
collect2: error: ld returned 1 exit status
1

顯然system方法只能獲取標準輸出,但是不能從標準輸出中提取內容。也許有人覺得用os.popen()執行bash命令,可以返回文件對象,解析提取最後一行的狀態碼:

import os
print('the status code is:',os.popen('gcc haha;echo $?').readlines()[-1])
----------------------------
stanpao@vm:~$ python test.py 
/usr/bin/ld:haha: file format not recognized; treating as linker script
/usr/bin/ld:haha:1: syntax error
collect2: error: ld returned 1 exit status
('the status code is:', '1\n')

這樣看來,也確實可以獲得程序的運行狀態碼,但是不難發現,如果在echo之前執行一條其他命名就會影響到對程序運行狀態碼的正常獲取——例如,需要將編譯程序運行的異常輸出寫道日誌文件中,使用命令‘>>log’將信息追加,顯然作爲標準輸出的重定向,這條命令只能在popen之後:

import os
print('the status code is:',os.popen('gcc haha >> log;echo $?').readlines()[-1])
-----------------------------
stanpao@vm:~$ python test.py 
/usr/bin/ld:haha: file format not recognized; treating as linker script
/usr/bin/ld:haha:1: syntax error
collect2: error: ld returned 1 exit status
('the status code is:', '1\n')
stanpao@vm:~$ more log 
stanpao@vm:~$ 

可見,仍有異常輸出,但是不存在標準輸入正確寫入文件。不妨試試tee將重定向到log中:

import os
print('the status code is:',os.popen('gcc haha | tee -a log ; echo $?').readlines()[-1])
----------------------------
stanpao@vm:~$ python test.py 
/usr/bin/ld:haha: file format not recognized; treating as linker script
/usr/bin/ld:haha:1: syntax error
collect2: error: ld returned 1 exit status
('the status code is:', '0\n')
stanpao@vm:~$ more log 
stanpao@vm:~$ 

此時,處於命令行原因,不僅不能正確獲取異常日誌信息的標準錯誤輸出,連狀態碼1也因爲正確執行了tee命令被錯誤記爲0.顯然,從bash命令角度獲取程序運行狀態,並不高效簡單。這裏介紹python強大subprocess模塊。

import subprocess
compilePopen = subprocess.Popen('gcc haha',shell=True,stderr=subprocess.PIPE)
compilePopen.wait()
print('the status code is:',compilePopen.returncode)
with open('log','w') as object:
    object.write(compilePopen.stderr.read())
-----------------------------
stanpao@vm:~$ python test.py 
('the status code is:', 1)
stanpao@vm:~$ more log 
/usr/bin/ld:haha: file format not recognized; treating as linker script
/usr/bin/ld:haha:1: syntax error
collect2: error: ld returned 1 exit status
stanpao@vm:~$ 

由此可見,使用subprocess不僅簡化了對bash命令的調用,還能高效獲取標準輸出內容跟子程序的狀態返回碼。下面介紹subprocess模塊的基礎用法。
在該模塊中,Popen是基類,其他的方法都是在參數衆多的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:shell命令,可以是字符串,或者序列類型,如list,tuple。
bufsize:緩衝區大小,可默認
stdin,stdout,stderr:分別表示程序的標準輸入,標準輸出及標準錯誤
shell:True可以將shell命令整個以字符串表示,False時,shell命令要split成序列
cwd:用於設置子進程的當前目錄
env:用於指定子進程的環境變量。如果env=None,則默認從父進程繼承環境變量
universal_newlines:不同系統的的換行符不同,當該參數設定爲true時,則表示使用\n作爲換行符

Popen類生成對象實例,在指定執行特定子程序之後,需要使用類方法wait()等待子程序結束,獲取子程序相關信息,比如子程序返回狀態碼,記爲Popen對象的returnCode屬性。在Popen類中指定stdout跟stdin可以指定子程序相關的標準輸出輸入來源,但是如果需要需要將這些輸入輸出內容進行讀寫修改,需要指定特殊參數subprocess.PIPE將內容納入管道緩存中,由Popen對象對應的屬性獲得其對應的文件對象,進行讀取修改操作,參考上述代碼stderr=subprocess.PIPE以及compilePopen.stderr部分,其中compilePopen爲Popen對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章