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
簡單用法:
# 第一步創建子程序,輸入執行的操作命令,我這里是以telnet遠程終端為例
child = pexpect.spawn('telnet ipaddress')
# 第二步從子命令返回的結果中進行匹配,語法:expect(pattern, timeout=-1),

  pattern參數說明:可以為字符串,正則表達式,EOF,TIMEOUT,或者是以上類型的列表,若只提供字符串,匹配成功后返回0,若提供列表,匹配成功后返回列表的索引序號。未匹配成功,則會引發異常

timeout:為-1 使用默認的超時期限,設置為NONE,將阻塞至返回信息
child.expect('password:')
# 第三步匹配成功后,發送交互信息,這里發送密碼
child.sendline(mypassword)

獲取命令執行結果主要由3個基本屬性。

child.before:輸出匹配點之前的命令執行結果

child.after:輸出匹配成功后的匹配點內容,如標識符:hostname#>

child.match:保存已匹配的匹配點對象,可以使用string,打印命令的執行結果。

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

 


免責聲明!

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



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