Python 實現 Shell 指令碼功能
最近生產環境上發現有伺服器程序出現任務堆積的情況,由於一時無法定位出原因,故對堆積的任務數量進行監控。程序日誌中已有任務數量的輸出,故只需要編寫一個指令碼讀取日誌中的任務數量,發現任務數量超過某個閾值就傳送告警簡訊即可。
本想使用 Shell 指令碼來實現,沒想到 Shell 的語法實在不好掌握,賦值語法,數值比較語法,字串與數字的轉換,等等,這些語法的問題經過了多次 google 和百度後,還是沒能解決:(。一氣之下,決定還是用回了自己熟悉的 Python 來實現。
Python 中執行 Shell 命令有多種方法,stackoverflow 上有對這些方法進行比較的討論,Calling an external command in Python
subprocess
模組來實現更優。因此,本文說明如何使用subprocess
模組來實現 Shell 指令碼的功能。 subprocess
模組提供多種方法來實現執行 Linux 的命令,例如subprocess.call()
方法,subprocess.check_call()
方法,等。這些方法都是對Popen
類的封裝,故本文著重講述Popen
類的使用。
執行 Shell 命令
可以通過向Popen()
傳遞需要執行的命令來建立一個Popen
物件,這樣,便會建立一個子程序來執行命令。例如:
child = subprocess.Popen(["ping","-c","5" ,"leehao.me"])
上面的程式碼會建立一個子程序來執行ping -c 5 leehao.me
命令,這個命令採用列表的形式傳遞給Popen()
方法。如果我們想直接採用ping -c 5 leehao.me
字串形式,可以新增shell=True
來實現:
child = subprocess.Popen("ping -c 5 leehao.me", shell=True)
官方文件指出由於安全原因故不建議使用shell=True
,詳細說明可以參考官方文件的描述。
等待子程序執行
子程序執行命令後,主程序並不會等待子程序執行。為了讓主程序等待子程序執行結束,需要顯示呼叫Popen.wait()
child = subprocess.Popen(["ping","-c","5","leehao.me"])
child.wait()
print 'parent finish'
這樣,主程序會等待子程序執行ping
命令完畢後,才會打印出parent finish
的輸出。
獲取執行結果
為了獲取Popen()
子程序的輸出,可以使用Popen.communicate()
方法,例如:
def subprocess_cmd(command):
process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True)
proc_stdout = process.communicate()[0].strip()
print proc_stdout
subprocess_cmd('echo leehao.me; echo www.leehao.me')
輸出:
leehao.me
www.leehao.me
process.communicate()
方法可以實現主程序與子程序的通訊。主程序可以通過它向子程序傳送資料,也可以讀取子程序的輸出的資料。上面的例子中,我們在建立Popen
物件時指定stdout=subprocess.PIPE
,這樣主程序便可以讀取子程序的輸出。
communicate()
方法返回一個元組:(stdoutdata, stderrdata)
,process.communicate()[0]
即獲取子程序的標準輸出。
需要指出的是,呼叫communicate()
方法後,主程序也會等待子程序執行完畢。
上面的例子中,子程序向標準輸出列印兩個字串,主程序接收到了這些輸出,並打印出來。
有了上面的基礎,實現我們的監控指令碼就易如反掌了,下面是我們的指令碼程式碼。為了免受騷擾,將手機號碼替換了:)
附:監控指令碼
from datetime import datetime
import subprocess
def safe_int(s):
try:
n = int(s)
except Exception, ex:
n = 0
return n
def run():
print 'begin to monitor task num, time: %s' % datetime.now()
child = subprocess.Popen('grep "socket進入佇列" /home/lihao/logs/ksb.txt | tail -n 1 | cut -d ":" -f 3',
shell=True, stdout=subprocess.PIPE)
out = child.communicate()[0]
out = out.strip()
print(out)
num = safe_int(out)
if num > 5:
print 'task num is over limit, num: %s, time: %s' % (num, datetime.now())
msg = '%s, 193 task num is over limit, task num: %s' % (datetime.now(), num)
cmd = ['/home/soft/SendMsg/SendMsg', '1', '13800138000', msg]
print cmd
child = subprocess.Popen(cmd, cwd="/home/soft/SendMsg")
child.wait()
else:
print 'task num is ok, num: %s, time: %s' % (num, datetime.now())
if __name__ == '__main__':
run()