使用subprocess进行外部命令调用
当我们写python脚本的时候,如果需要调用外部命令,有很多的模板可以使用,标准包里面的os.popen,os.system,commands.getstatusoutput. 但是这些现在都是不推荐的了,现在标准包推荐使用的是subprocess模块. 我一般会使用如下函数来进行封装
def run(cmd): p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) out, err = p.communicate() print ('cmd=[{}] out=[{}] err=[{}] returncode=[{}]'.format(cmd, out, err, p.returncode)) if p.returncode != 0: raise Exception('cmd run error') return out
这样就可以直接调用run函数来运行命令,在返回码为0(Linux下面被认为是命令执行正确)的情况下,得到命令的标准输出. 在返回码非0(linux下面被认为是命令执行错误)的情况下,直接抛出异常.
这儿抛出异常而不是直接返回其他特殊的值,比如None. 这样可以让脚本在有问题的时候直接退出,而不是让这个错误传递下去.
Linux里面有些特殊的命令,在正常情况下返回码非0,那么自己可以用try,catch把run函数包起来.
使用subprocess32模块来替代subprocess模块
在python2.7中,如果只是在单线程中使用subprocess模板,会发现一切工作正常.但是当在多线程中使用subprocess模块的时候,在某些情况下,一些线程会直接夯住不动.我第一次遇到这个问题排查了特别久.最后google发现是python 2.7中subprocess模块自己的问题.而这个问题在python3中是解决的,google的工程师将python3中的代码backport到python2.7中,创建了subprocess32项目.其pypi链接为https://pypi.org/project/subprocess32/.项目描述中解释了fork和exec之间处理了一些subprocess中没有解决的问题,让整个模板在多线程下工作更加稳定
使用pip install subprocess32就可以安装.
注意这个包只能在Linux/Unix中使用,Windows中不行.
要使用也比较简单,就是先尝试导入subprocess32为subprocess,如果不成功,那么直接导入subporcess
其他选择
大家如果需要在python2.7里面调用外部命令,可以尝试一下sh模块,这个模块和subprocess的使用方法比较不同.
subprocess在加上shell=True的情况下,就相当于在bash里面敲入命令,用起来比较直接.
sh模块通过延迟加载,可以让PATH里面的命令看起来都像是python里面的函数一样被运行,用起来感觉不太一样.