在python腳本中執(zhí)行shell命令,可能是平常寫腳本過程中比較常見的一種場景,這兩天在寫程序的時候發(fā)現(xiàn)一個問題,這里分享一下。
就是寫python的時候,如果執(zhí)行了一個命令,我們想知道它的返回,一般是可以通過commands這個模塊來處理的,我比較習(xí)慣使用的是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)
--------結(jié)果------
start_time is: 2021-03-07 23:24:32
status is 0,result is
end_time is: 2021-03-07 23:24:42
上面的例子中,我們執(zhí)行了一個sleep 10的shell操作。將執(zhí)行的狀態(tài)保存在status變量中,將執(zhí)行結(jié)果保存在result變量中。
結(jié)果中可以看到status返回0,而result返回空值。我們腳本執(zhí)行的開始時間start_time和腳本的最后輸出時間end_time中間,有10s的時間差。
如果我們有一個shell命令,需要執(zhí)行很久,例如1個小時,這個時候,如果我們用commands.getstatusoutput模塊的話,只能等這個python腳本執(zhí)行完,才能得到shell腳本最終的執(zhí)行結(jié)果。
因為執(zhí)行的時間很長,所以有時候我們想要查詢執(zhí)行的中間狀態(tài)來確保當(dāng)前程序依舊在執(zhí)行中,而不是掛掉了。commands.getstatusoutput是不能查看命令執(zhí)行的中間狀態(tài)的。
這里我們需要引入另外一個模塊,subpress模塊,這個模塊中有一個類Popen,可以查看當(dāng)前命令執(zhí)行過程中間的狀態(tài),如下:
-----代碼片段----
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())
----執(zhí)行結(jié)果-----
proc is <subprocess.Popen object at 0x7faff43f0e10>
5s result is : None
10s tmp result is : 0
可以看到,我們引用subpress模塊的Popen類來執(zhí)行sleep 10這個shell命令。這個Popen類還包含一個poll的函數(shù),來查看當(dāng)前命令的返回值。如上述結(jié)果:
5s的時候,輸出的結(jié)果是None,代表sleep 10還在執(zhí)行中;
10s的時候,輸出的結(jié)果是0,代表sleep 10執(zhí)行成功。
這樣就可以捕捉命令的執(zhí)行中間過程,我們完全可以寫一個while 1的循環(huán),等待某個命令的返回值為0,在開始執(zhí)行后面的命令。還可以將執(zhí)行的中間結(jié)果打印到屏幕上,避免執(zhí)行命令的時候,完全是一個黑盒狀態(tài)。
這個Popen類中還有其他很有用的函數(shù),這里簡要介紹,有需求可以去研究下:
Popen.poll() | 用于檢查子進程(命令)是否已經(jīng)執(zhí)行結(jié)束,沒結(jié)束返回None,結(jié)束后返回狀態(tài)碼。 |
Popen.wait(timeout=None) | 等待子進程結(jié)束,并返回狀態(tài)碼;如果在timeout指定的秒數(shù)之后進程還沒有結(jié)束,將會拋出一個TimeoutExpired異常。 |
Popen.communicate(input=None, timeout=None) | 該方法可用來與進程進行交互,比如發(fā)送數(shù)據(jù)到stdin,從stdout和stderr讀取數(shù)據(jù),直到到達文件末尾。 |
Popen.send_signal(signal) | 發(fā)送指定的信號給這個子進程。 |
Popen.terminate() | 停止該子進程。 |
Popen.kill() | 殺死該子進程。 |