python 終端模擬模組 pexpect
簡單介紹
pexpect是 Don Libes 的 Expect 語言的一個 Python 實現,是一個用來啟動子程式,並使用正則表示式對程式輸出做出特定響應,以此實現與其自動互動的 Python 模組。它可以用來實現與ssh, ftp, telnet等程式的自動互動,參考官方文件:https://pexpect.readthedocs.io/en/stable/
安裝均可以使用pip進行
注意:windows和linux安裝模組是不同的。如下:
在linux下安裝:pip3 install pexpect
在windows下安裝: pip3.exe install winpexpect
在windows系統裡建立子程式是:
import winpexpect
child = winpexpect.winspawn('command')
另:run(), pxssh()均不可使用
在linux系統裡建立子程式是:
import pexpect
child = pexpect.spawn('cmd')
模組內主要的類/函式介紹
spawn / winspawn 類
作用:可以實現更復雜的互動,通過生成子程式進行sendline(傳送命令)與expect(返回操作符)進行互動。
class spawn
def __init__(self, command, args=[], timeout=30, maxread=2000,
searchwindowsize=None, logfile=None, cwd=None, env=None,
username=None, domain=None, password=None)
個別引數解釋:
timeout:互動等待的超時值,預設30秒
maxread:設定read buffer大小,
searchwindowsize:從輸入緩衝區中進行模式匹配的位置,預設從開始匹配。
logfile:指定日誌的記錄位置
注意:pexpect不支援管道,重定向或萬用字元,如果需要使用,需要重新開啟一個shell
舉例:
# 第一步與終端建立連線
child = pexpect.spawn('telnet ipaddress')
# 第二步等待終端返回特定內容: password:
child.expect('password:')
# 第三步根據返回內容傳送命令進行互動
child.sendline(mypassword)
pxssh類
是pexpect的派生類,用於建立ssh連線,比pexpect直接使用ssh時簡單一些,內建3個方法:
login() 建立到目標機器的ssh連線;
logout() 釋放該連線;
prompt() 等待提示符,通常用於等待命令執行結束。
pexpect.EOF 異常錯誤
獲取pexpect錯誤資訊,可能會有兩種 EOF 異常被丟擲,但是他們除了顯示的資訊不同,其實本質上是相同的。為了實用的目的,不需要區分它們,他們只是給了些關於你的 python 程式到底執行在哪個平臺上的額外資訊,這兩個顯示資訊是:
End Of File (EOF) in read(). Exception style platform.
End Of File (EOF) in read(). Empty string style platform.
有些 UNIX 平臺,當你讀取一個處於 EOF 狀態的檔案描述符時,會丟擲異常,其他 UNIX 平臺,卻只會靜靜地返回一個空字串來表明該檔案已經達到了狀態。
pexpect.TIMEOUT異常錯誤
如果子程式沒有在指定的時間內生成任何 output,那麼 expect() 和 read() 都會產生 TIMEOUT 異常。超時預設是 30s,可以在 expect() 和 spawn 建構函式初始化時指定為其它時間
child.expect('password:', timeout=120) # 等待 120s
如果你想讓 expect() 和 read() 忽略超時限制,即無限期阻塞住直到有 output 產生,設定 timeout 引數為 None。
程式碼如:
child = pexpect.spawn( "telnet 域名" )
child.expect( "login", timeout=None )
以上兩個錯誤可以使用try語句來處理報錯
try:
child=pexpect.spawn('telnet ip',timeout=5)
child.logfile = log
child.logfile_send=sys.stdout
child.expect("New password:")
child.sendline(mypassword)
child.expect("Retype new password:")
child.sendline(mypassword)
child.expect("passwd: all authentication tokens updated successfully.")
except pexpect.EOF:
pass
except pexpect.TIMEOUT:
pass
例項:自動登陸ftp
1 import pexpect 2 # 即將 ftp 所要登入的遠端主機的域名 3 ipAddress = '域名名稱' 4 # 登入使用者名稱 5 loginName = 'username' 6 # 使用者名稱密碼 7 loginPassword = 'password' 8 # 拼湊 ftp 將要執行的命令 9 cmd = 'ftp ' + ipAddress 10 # 利用 cmd 命令作為 spawn 類建構函式的引數,生成一個 spawn 類的物件 11 child = pexpect.spawn(cmd) 12 # 期望具有提示輸入使用者名稱的字元出現 13 index = child.expect(["(?i)name", "(?i)Unknown host", pexpect.EOF, pexpect.TIMEOUT]) 14 # 匹配到了列表索引為0的字串 "(?i)name",表明接下來要輸入使用者名稱 15 if index == 0: 16 # 傳送登入使用者名稱 + 換行符給子程式. 17 child.sendline(loginName) 18 # 期望 "(?i)password" 具有提示輸入密碼的字元出現. 19 index = child.expect(["(?i)password", pexpect.EOF, pexpect.TIMEOUT]) 20 # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程式列印提示資訊並退出. 21 if (index != 0): 22 print "ftp login failed" 23 child.close(force=True) 24 # 匹配到了密碼提示符,傳送密碼 + 換行符給子程式. 25 child.sendline(loginPassword) 26 # 期望登入成功後,提示符 "ftp>" 字元出現. 27 index = child.expect( ['ftp>', 'Login incorrect', 'Service not available', 28 pexpect.EOF, pexpect.TIMEOUT]) 29 # 匹配到了 'ftp>',登入成功. 30 if (index == 0): 31 print 'Congratulations! ftp login correct!' 32 # 傳送 'bin'+ 換行符給子程式,表示接下來使用二進位制模式來傳輸檔案. 33 child.sendline("bin") 34 print 'getting a file...' 35 # 向子程式傳送下載檔案 rmall 的命令. 36 child.sendline("get rmall") 37 # 期望下載成功後,出現 'Transfer complete.*ftp>',其實下載成功後, 38 # 會出現以下類似於以下的提示資訊: 39 # 200 PORT command successful. 40 # 150 Opening data connection for rmall (548 bytes). 41 # 226 Transfer complete. 42 # 548 bytes received in 0.00019 seconds (2.8e+03 Kbytes/s) 43 # 所以直接用正則表示式 '.*' 將 'Transfer complete' 和提示符 'ftp>' 之間的字元全省去. 44 index = child.expect( ['Transfer complete.*ftp>', pexpect.EOF, pexpect.TIMEOUT] ) 45 # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程式列印提示資訊並退出. 46 if (index != 0): 47 print "failed to get the file" 48 child.close(force=True) 49 # 匹配到了 'Transfer complete.*ftp>',表明下載檔案成功,列印成功資訊,並輸入 'bye',結束 ftp session. 50 print 'successfully received the file' 51 child.sendline("bye") 52 # 使用者名稱或密碼不對,會先出現 'Login incorrect',然後仍會出現 'ftp>',但是 pexpect 是最小匹配,不是貪婪匹配, 53 # 所以如果使用者名稱或密碼不對,會匹配到 'Login incorrect',而不是 'ftp>',然後程式列印提示資訊並退出. 54 elif (index == 1): 55 print "You entered an invalid login name or password. Program quits!" 56 child.close(force=True) 57 # 匹配到了 'Service not available',一般表明 421 Service not available, remote server has closed connection,程式列印提示資訊並退出. 58 # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程式列印提示資訊並退出. 59 else: 60 print "ftp login failed! index = " + index 61 child.close(force=True)View Code
例項:自動登陸交換機執行一條命令後退出,最好在linux系統下進行測試,在windows下使用winpexpect不好用
1 #!/opt/python3/bin/python3 2 # _*_ coding:utf-8 _*_ 3 # Author: Yong 4 5 import pexpect 6 # 基本資訊 7 ipAddr = '交換機IP' 8 pwd = '交換機登陸密碼' 9 cmd = 'telnet ' + ipAddr 10 11 # 連線裝置,建立子程序 12 child = pexpect.spawn(cmd, timeout=300) 13 # 進行匹配返回識別符號 14 index = child.expect(['Password:', pexpect.EOF, pexpect.TIMEOUT]) 15 # 匹配到列表索引0的內容 16 if index == 0: 17 # 網路裝置登陸直接提示密碼,傳送密碼 18 child.sendline(pwd) 19 # 匹配返回識別符號 20 index = child.expect('<A-3F-30>') 21 # 未匹配到識別符號,退出 22 if index != 0: 23 exit('密碼錯誤') 24 print('密碼驗證完成') 25 # 傳送執行的命令 26 child.sendline('display version') 27 # 匹配返回的識別符號 28 child.expect('<A-3F-30>') 29 # 列印命令執行結果 30 print(child.before) 31 # print('將telnet子程式執行權交給使用者') 32 # child.interact() 33 34 else: 35 # 匹配到EOF 或者TIMEOUT表示超過時間限制 36 print('telnet faild, EOF OR TIMEOUT',pexpect.EOF) 37 # 關閉連線 38 child.close()View Code