python腳本的實例
本文通過一個簡單的python腳本實例,來介紹python語法。
參數:ip地址,端口號port
需求:
- 首先進行ping,如果ping不成功就就進行traceroute
- 如果ping得通就行telnet
- traceroute的結果要輸出最後一跳的地址
- telnet如果成功輸出成功,不成功且失敗信息報refused則輸出“端口未開啓”,如果不成功且失敗信息沒有報refused則報訪問不通
- 需要進行一個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)
主要參數說明:
- args:args should be a string, or a sequence of program arguments.也就是說必須是一個字符串或者序列類型(如:字符串、list、元組),用於指定進程的可執行文件及其參數。如果是一個序列類型參數,則序列的第一個元素通常都必須是一個可執行文件的路徑。當然也可以使用executeable參數來指定可執行文件的路徑。
- stdin,stdout,stderr:分別表示程序的標準輸入、標準輸出、標準錯誤。有效的值可以是PIPE,存在的文件描述符,存在的文件對象或None,如果爲None需從父進程繼承過來,stdout可以是PIPE,表示對子進程創建一個管道,stderr可以是STDOUT,表示標準錯誤數據應該從應用程序中捕獲並作爲標準輸出流stdout的文件句柄。
- shell:如果這個參數被設置爲True,程序將通過shell來執行。
- env:它描述的是子進程的環境變量。如果爲None,子進程的環境變量將從父進程繼承而來。
創建Popen類的實例對象
-
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
cmd:標準像子進程傳入需要執行的shell命令,如:ls -al
-
subprocess.PIPE:在創建Popen對象時,subprocess.PIPE可以初始化爲stdin, stdout 或stderr的參數,表示與子進程通信的標準輸入流,標準輸出流以及標準錯誤
-
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,執行該函數的時候會把信息直接打印出來。並且我們無法以數據結構的形式保存結果,因此對於需要後續邏輯判斷的函數就不適用了。