1. 程式人生 > >python 終端模擬模組 pexpect

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