在python脚本中执行shell命令,可能是平常写脚本过程中比较常见的一种场景,这两天在写程序的时候发现一个问题,这里分享一下。
就是写python的时候,如果执行了一个命令,我们想知道它的返回,一般是可以通过commands这个模块来处理的,我比较习惯使用的是commands.getstatusoutput,来看个例子:
-----代码片段----- import commands import datetime start_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') print 'start_time is: {}'.format(start_time) status,result = commands.getstatusoutput('sleep 10') print "status is {0},result is {1}".format(status,result) end_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') print 'end_time is: {}'.format(end_time) --------结果------ start_time is: 2021-03-07 23:24:32 status is 0,result is end_time is: 2021-03-07 23:24:42
上面的例子中,我们执行了一个sleep 10的shell操作。将执行的状态保存在status变量中,将执行结果保存在result变量中。
结果中可以看到status返回0,而result返回空值。我们脚本执行的开始时间start_time和脚本的最后输出时间end_time中间,有10s的时间差。
如果我们有一个shell命令,需要执行很久,例如1个小时,这个时候,如果我们用commands.getstatusoutput模块的话,只能等这个python脚本执行完,才能得到shell脚本最终的执行结果。
因为执行的时间很长,所以有时候我们想要查询执行的中间状态来确保当前程序依旧在执行中,而不是挂掉了。commands.getstatusoutput是不能查看命令执行的中间状态的。
这里我们需要引入另外一个模块,subpress模块,这个模块中有一个类Popen,可以查看当前命令执行过程中间的状态,如下:
-----代码片段---- import subprocess import datetime import time proc = subprocess.Popen("sleep 10",shell=True, stdout=subprocess.PIPE) print "proc is {}".format(proc) time.sleep(5) print '5s result is : {}'.format(proc.poll()) time.sleep(10) print '10s tmp result is : {}'.format(proc.poll()) ----执行结果----- proc is <subprocess.Popen object at 0x7faff43f0e10> 5s result is : None 10s tmp result is : 0
可以看到,我们引用subpress模块的Popen类来执行sleep 10这个shell命令。这个Popen类还包含一个poll的函数,来查看当前命令的返回值。如上述结果:
5s的时候,输出的结果是None,代表sleep 10还在执行中;
10s的时候,输出的结果是0,代表sleep 10执行成功。
这样就可以捕捉命令的执行中间过程,我们完全可以写一个while 1的循环,等待某个命令的返回值为0,在开始执行后面的命令。还可以将执行的中间结果打印到屏幕上,避免执行命令的时候,完全是一个黑盒状态。
这个Popen类中还有其他很有用的函数,这里简要介绍,有需求可以去研究下:
Popen.poll() | 用于检查子进程(命令)是否已经执行结束,没结束返回None,结束后返回状态码。 |
---|---|
Popen.wait(timeout=None) | 等待子进程结束,并返回状态码;如果在timeout指定的秒数之后进程还没有结束,将会抛出一个TimeoutExpired异常。 |
Popen.communicate(input=None, timeout=None) | 该方法可用来与进程进行交互,比如发送数据到stdin,从stdout和stderr读取数据,直到到达文件末尾。 |
Popen.send_signal(signal) | 发送指定的信号给这个子进程。 |
Popen.terminate() | 停止该子进程。 |
Popen.kill() | 杀死该子进程。 |