利用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对象。

你可能感兴趣的:(Python)