1. 程式人生 > >Python 實現 Shell 指令碼功能

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()

參考資料