簡單介紹
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)
實例:自動登陸交換機執行一條命令后退出,最好在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()