使用Python實時獲取cmd的輸出


最近發現一個問題,一個小伙兒寫的console程序不夠健壯,監聽SOCKET的時候容易崩,造成程序的整體奔潰,無奈他沒有找到問題的解決辦法,一直解決不了,可是這又是一個監控程序,還是比較要緊的,又必須想辦法解決。

(這是要搞死我的節奏啊....)由於個人不太懂他用的語言,只能在程序外圍想辦法。

 

環境描述:

1. 目標程序執行時會監聽8080端口,TCP,並在每一次client連接后通過console輸出client的IP地址。

2. 監聽不是一次性完成的,而是一直監聽,程序並不會退出

3. 為了監控需要,最好能對連接的IP進行排序,整理。

P.S. 系統基於windows平台。

 

想起來做監控程序,簡單點比較好,於是想到了Python。

我的預想邏輯是這樣的,通過python檢測目標程序是否崩了,如果中標就啟動目標程序,並進行監控,每輸出一次,python進行一次數據運算整理,然后循環。

第一步,先搞定輸出的捕獲問題。

# this method is used for monitoring

import time
import subprocess
import locale
import codecs

mylist = []
ps = subprocess.Popen('netstat -a', stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
while True:
    data = ps.stdout.readline()
    if data == b'':
        if ps.poll() is not None:
            break
    else:
        mylist.append(data.decode(codecs.lookup(locale.getpreferredencoding()).name))
        newlist = []
        for i in mylist:
            if i.find('192.168') > 0:
                newlist.append(i)
        newlist.sort()
        print('Sum of requests from LAN:', len(newlist))

 

我用netstat -a替代那個需要持續輸出的程序,執行程序,發現程序和想象的不太一樣,確實是實時獲得數據了,但是感覺總是有點不太和諧,不管了,繼續。

第二步,解決監控程序的問題

程序或者還是死的,有一點非常關鍵,就是監聽端口,那只要檢測一下端口就行了。三個辦法:

1. 找端口檢測的API

2. 連接一次目標端口,通了就是活的

3. netstat

第一種方法需要去找找有沒有相關的API,第二種方法容易對目標程序的正常運行造成問題,第三種我想都沒想就用了吧。這里需要用到cmd的重定向功能

# this method is used for monitoring

import time
import subprocess
import locale
import codecs


def getstdout(p):
    mylist = []
    while True:
        data = p.stdout.readline()
        if data == b'':
            if p.poll() is not None:
                break
        else:
            mylist.append(data.decode(codecs.lookup(locale.getpreferredencoding()).name))
    return mylist

while True:
    ps = subprocess.Popen('netstat -an | findstr "8080"', stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
    resultlist = getstdout(ps)
    if len(resultlist) >= 1:
        pass
    else:
        print(time.strftime("%Y-%m-%d %H:%M:%S"))
        subprocess.Popen('taskkill.exe /f /im node.exe', shell=False)
     # 防止動作過快,把新建的程序整死了 time.sleep(
3) subprocess.Popen('start node D:\\app.js', shell=True) time.sleep(10)

netstat -an獲得當前的端口監聽情況,“|”將netstat的輸出重定向到findstr函數

netstat -an | findstr "8080" 查找有8080端口的地址行,有就說明活着,否則就是掛了。

 

最后一步,整合

# this method is used for monitoring

import time
import subprocess
import locale
import codecs


def getstdout(p):
    mylist = []
    while True:
        data = p.stdout.readline()
        if data == b'':
            if p.poll() is not None:
                break
        else:
            mylist.append(data.decode(codecs.lookup(locale.getpreferredencoding()).name))
    return mylist

while True:
    ps = subprocess.Popen('netstat -an | findstr "8080"', stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
    resultlist = getstdout(ps)
    if len(resultlist) >= 1:
        pass
    else:
        print(time.strftime("%Y-%m-%d %H:%M:%S"))
        subprocess.Popen('taskkill.exe /f /im node.exe', shell=False)
        time.sleep(3)
        pss = subprocess.Popen('start cmd.exe /k node app.js', stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE, shell=True)
        alist = getstdout(pss)
        newlist = []
        for i in alist:
            if i.find('192.168') > 0:
                newlist.append(i)
        newlist.sort()
        print('Sum of requests from LAN:', len(newlist))
    time.sleep(10)

然后發現有問題,程序完全不會定時檢測,只會卡在readline()上。

各種找問題,發現那個process.stdout.readline()是個同步方法,沒結果就不返回。有沒有的能異步的方法?

有人用fnctl,windows不支持,pass

asyncio?看了半天沒太明白...

折騰了半天,最后關頭我還是用c#解決這個問題了....

參考代碼見http://www.jiamaocode.com/Cts/1031.html,打不開的話http://www.cnblogs.com/sode/archive/2012/07/10/2583941.html有轉載

 

總算解決了這個問題,但是我心中還是不爽,思考了很久如何解決異步readline()的問題。忽然想起來多線程這個利器,干脆開一個線程,不返回就等着,不就問題解決了。

# this method is used for monitoring

import time
import subprocess
import locale
import codecs
import threading


alist = []


def getstdout(p, asy):
    if asy:
        alist.clear()
    mylist = []
    while True:
        data = p.stdout.readline()
        if data == b'':
            if p.poll() is not None:
                break
        else:
            if asy:
                alist.append(data.decode(codecs.lookup(locale.getpreferredencoding()).name))
            else:
                mylist.append(data.decode(codecs.lookup(locale.getpreferredencoding()).name))
    return mylist

while True:
    ps = subprocess.Popen('netstat -an | findstr "8080"', stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
    resultlist = getstdout(ps, False)
    if len(resultlist) >= 1:
        newlist = []
        for i in alist:
            if i.find('192.168') > 0:
                newlist.append(i)
        newlist.sort()
        print('Sum of requests from LAN:', len(newlist))
    else:
        print(time.strftime("%Y-%m-%d %H:%M:%S"))
        subprocess.Popen('taskkill.exe /f /im node.exe', shell=False)
        time.sleep(3)
        pss = subprocess.Popen('start cmd.exe /k node app.js', stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE, shell=True)
        th = threading.Thread(target=getstdout, args=[pss, True])
        th.start()
    time.sleep(10)
View Code

 

總結

有時候簡單的解決方法也可以實現同樣的功能,對比python的實現與C#的實現,C#更面向事件一點,python應該也有不錯的解決方案,繼續摸索...

P.S. 注意cmd輸出在UNICODE系統是b''這種類型的字符串,轉碼對自己系統的默認編碼不清楚的建議用codecs.lookup(locale.getpreferredencoding()).name,貿然用utf-8各種坑。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM