【Python】python腳本實例

python腳本的實例

本文通過一個簡單的python腳本實例,來介紹python語法。
參數:ip地址,端口號port
需求:

  1. 首先進行ping,如果ping不成功就就進行traceroute
  2. 如果ping得通就行telnet
  3. traceroute的結果要輸出最後一跳的地址
  4. telnet如果成功輸出成功,不成功且失敗信息報refused則輸出“端口未開啓”,如果不成功且失敗信息沒有報refused則報訪問不通
  5. 需要進行一個ip校驗,看是否符合格式

1.首先編寫ip地址檢驗的方法

import sys
import re

def checkIp(host):
    ip = host
    # 檢驗ip地址中的正則
    res = re.match(r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", ip)
    if res:
        print ("ip is ok")
        return 0
    else:
        print ("ip is error")
        return 1

ip地址的檢驗邏輯非常簡單,只要用正則表達式就可以。這裏介紹一下python正則中的“原始字符串”功能。

反斜槓,在Python中比較特殊,就是它可以用來構成一些特殊字符,比如“\n”表示換行,“\t”表示製表符。下面是使用“\n”的一行代碼:

>>>print ('Hello\World\nPython' )
結果爲:
“Hello\World
Python“

可以看到其中的“\n”已轉義爲換行符,而“\W”沒有發生轉義,原因是“\W”在“字符串轉義”中並不對應着特殊字符,沒有特殊含義。

如果現在要求變了,要求不對“\n”轉義爲換行,而是原封不動輸出爲“Hello\World\nPython”,該怎麼辦呢?

1)可以這樣寫“Hello\World\nPython”,這樣輸出的時候,“字符串轉義”會把“\”轉義爲“\”;
2)也可使用另一種方法:原始字符串;原始字符串(即r’…’):字符串中所有字符都直接按照字面意思來使用,不轉義特殊字符。
下面是使用原始字符串的代碼:

 print r'Hello\World\nPython' 
 結果爲:
 “Hello\World\nPython”

可以清楚看到,在使用原始字符串之後,“\n”未被轉義爲換行符,而是直接被輸出了。

1.1正則轉義

上面講的只是“字符串轉義”。同理,在正則表達式中也存在轉義,稱其爲“正則轉義”,其與“字符串轉義”完全不同,比如“\d”代表數字,“\s”代表空白符。下面我們先編寫開頭的例子,然後再分析。
需求:提取“3\8”反斜槓之前的數字:

import re 
string = '3\8' 
m = re.search('(\d+)\\\\', string) 

if m is not None: 
    print m.group(1)                  # 結果爲:3 
n = re.search(r'(\d+)\\', string) 
if n is not None: 
    print n.group(1)                  # 結果爲:3

正則表達式字符串需要經過兩次轉義,這兩次分別是上面的“字符串轉義”和“正則轉義”,個人認爲“字符串轉義”一定先於“正則轉義”。

1)’\\\\'的過程:
先進行“字符串轉義”,前兩個反斜槓和後兩個反斜槓分別被轉義成了一個反斜槓;即“\|\”被轉成了“\|\”(“|”爲方便看清,請自動忽略)。“字符串轉義”後馬上進行“正則轉義”,“\\”被轉義爲了“\”,表示該正則式需要匹配一個反斜槓。

2)r’\\'的過程:
由於原始字符串中所有字符直接按照字面意思來使用,不轉義特殊字符,故不做“字符串轉義”,直接進入第二步“正則轉義”,在正則轉義中“\\”被轉義爲了“\”,表示該正則式需要匹配一個反斜槓。

也就是說原始字符串(即r’…’)與“正則轉義”毫無關係,原始字符串僅在“字符串轉義”中起作用,使字符串免去一次轉義。

我們再來看這個檢驗ip地址的正則表達式

r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"

用圓括號將所有選擇項括起來,相鄰的選擇項之間用|分隔,表示可以匹配25[0-5]或者2[0-4][0-9]或者[01]?[0-9][0-9]?,第一個就是250~255,第二個是200-249,第三個稍微複雜,?表示匹配0次或者1次前面的字符串,表示0-199。同時^表示匹配開頭,配合{3}表示匹配3次,表示前面三個字符串爲0.0.0.到255.255.255.。最後一個字符串用$匹配結尾。但用圓括號會有一個副作用,使相關的匹配會被緩存,導致匹配出現問題。此時可用?:放在第一個選項前來消除這種副作用。

因此該正則表達式的作用就是匹配0.0.0.0~255.255.255.255

2.traceroute函數編寫

def trace(host):
    cmd = "traceroute {}".format(host)
    # 創建一個subproess實例
    p = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
    result = p.stdout.readlines()   # 讀取輸出
    print(type(result))                   # 查看類型
    out = result[-1]                       # 取得最後一跳地址的string
    res = out.split()                      # 將最後一跳的string按照空格分隔
    print (res[2])       
    return res[2]

可以看到這裏使用了一個subprocess模塊的使用,允許你去創建一個新的進程讓其執行另外的程序,並與它進行通信,獲取標準的輸入、標準輸出、標準錯誤以及返回碼等。

2.1Popen類

subprocess模塊中定義了一個Popen類,通過它可以來創建進程,並與其進行復雜的交互。我們可以用它來調用shell來在Linux系統中進行交互,查看一下它的構造函數:

__init__(self, args, bufsize=0, executable=None, 
stdin=None, stdout=None, stderr=None, preexec_fn=None, 
close_fds=False, shell=False, cwd=None, env=None, 
universal_newlines=False, startupinfo=None, 
creationflags=0)

主要參數說明:

  1. args:args should be a string, or a sequence of program arguments.也就是說必須是一個字符串或者序列類型(如:字符串、list、元組),用於指定進程的可執行文件及其參數。如果是一個序列類型參數,則序列的第一個元素通常都必須是一個可執行文件的路徑。當然也可以使用executeable參數來指定可執行文件的路徑。
  2. stdin,stdout,stderr:分別表示程序的標準輸入、標準輸出、標準錯誤。有效的值可以是PIPE,存在的文件描述符,存在的文件對象或None,如果爲None需從父進程繼承過來,stdout可以是PIPE,表示對子進程創建一個管道,stderr可以是STDOUT,表示標準錯誤數據應該從應用程序中捕獲並作爲標準輸出流stdout的文件句柄。
  3. shell:如果這個參數被設置爲True,程序將通過shell來執行。
  4. env:它描述的是子進程的環境變量。如果爲None,子進程的環境變量將從父進程繼承而來。

創建Popen類的實例對象

  1. res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

  2. cmd:標準像子進程傳入需要執行的shell命令,如:ls -al

  3. subprocess.PIPE:在創建Popen對象時,subprocess.PIPE可以初始化爲stdin, stdout 或stderr的參數,表示與子進程通信的標準輸入流,標準輸出流以及標準錯誤

  4. subprocess.STDOUT:作爲Popen對象的stderr的參數,表示將標準錯誤通過標準輸出流輸出。

因此我們可以看到p = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
中調用shell的命令來執行
利用type方法來判斷result的類型,其中readlines()返回的是shell執行結果的list形式,二read()或者readline()返回的是string類型。我們需要得到traceroute的最後一跳結果,因此需要list類型的result,使用[-1]的切片來得到list的最後一個元素

result = p.stdout.readlines()  #返回一個list
print(type(result))
輸出結果
traceroute to 192.168.229.2 (192.168.229.2), 30 hops max, 60 byte packets
1  192.168.229.1
2  192.168.229.2
3  192.168.229.3
4  192.168.229.4
5  192.168.229.5
out = result[-1]
輸出結果
5  192.168.229.5

3.telnet函數的編寫

def do_telnet(Host, port):
    cmd = "telnet {} {}".format(Host,port)
    out = commands.getstatusoutput(cmd)  # 輸出的是元組
    print out
    index = out.index(256) 
    
    # 如果返回的結果中沒有256,則證明返回碼不是256,證明telnet成功
    if(index < 0):
        print ("telnet成功")
    # 如果telnet失敗,且失敗信息報refused,則輸出檢查機構端口
    elif(out[1].find('refused') > -1):
        print ("機構端口未開啓,請檢查貴司端口是否啓用")
    # 如果失敗信息沒有報refused,則需要進行traceroute
    else:
        print ("Telnet不通,訪問結果:xxx")

commands.getstatusoutput和subprocess方法一樣也是調用shell命令。這個方法唯一的優點是,它不是一個阻塞的方法並且能夠獲得執行的返回碼。而上面提到的subprocess的Popen方法會有阻塞的問題,就是它是一個阻塞的方法。如果運行cmd時產生的內容非常多,函數非常容易阻塞住。

3.ping函數的編寫

def pingIp(host):
    ip = host
    res = checkIp(ip)
    if res:
        return 1
    else:
        backinfo = os.system('ping -c 5 -w 1 %s' % ip)
        # 如果ping不通,進入traceroute
        if backinfo:
            lastHop = trace(ip)
            print ("網絡訪問不通,traceroute最後一跳地址爲{}".format(lastHop))
        # 如果ping通進行telnet
        else:
            do_telnet(ip,port)

這裏介紹第三個可以調用shell的python模塊,就是os.system,執行該函數的時候會把信息直接打印出來。並且我們無法以數據結構的形式保存結果,因此對於需要後續邏輯判斷的函數就不適用了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章