Pexpect 是一個用來啟動子程序並對其進行自動控制的純 Python 模塊。 Pexpect 可以用來和像 ssh、ftp、passwd、telnet 等命令行程序進行自動交互。繼第一部分《探索 Pexpect,第 1 部分:剖析 Pexpect 》介紹了 Pexpect 的基礎和如何使用后,本文將結合具體實例入手,詳細介紹 Pexpect 的用法和在實際應用中的注意點。
概述
通過本系列第一部分《探索 Pexpect,第 1 部分:剖析 Pexpect 》(請參閱參考資料)的介紹,相信大家已經對 Pexpect 的用法已經有了比較全面的了解,知道 Pexpect 是個純 Python 語言實現的模塊,使用其可以輕鬆方便的實現與 ssh、ftp、passwd 和 telnet 等程序的自動交互,但是讀者的理解還可能只是停留在理論基礎上,本文將從實際例子入手具體介紹 Pexpect 的使用場景和使用心得體驗,實例中的代碼讀者都可以直接拿來使用,相信會對大家產生比較大的幫助。以下是本文所要介紹的所有 Pexpect 例子標題:
![]() ![]() |
例 1:ftp 的使用
本例實現了如下功能:ftp 登錄到 develperWorks.ibm.com 主機上,並用二進位傳輸模式下載一個名叫 rmall的文件。
#!/usr/bin/env python import pexpect # 即將 ftp 所要登錄的遠程主機的域名 ipAddress = 'develperWorks.ibm.com' # 登錄用戶名 loginName = 'root' # 用戶名密碼 loginPassword = 'passw0rd' # 拼湊 ftp 命令 cmd = 'ftp ' + ipAddress # 利用 ftp 命令作為 spawn 類構造函數的參數,生成一個 spawn 類的對象 child = pexpect.spawn(cmd) # 期望具有提示輸入用戶名的字元出現 index = child.expect(["(?i)name", "(?i)Unknown host", pexpect.EOF, pexpect.TIMEOUT]) # 匹配到了 "(?i)name",表明接下來要輸入用戶名 if ( index == 0 ): # 發送登錄用戶名 + 換行符給子程序. child.sendline(loginName) # 期望 "(?i)password" 具有提示輸入密碼的字元出現. index = child.expect(["(?i)password", pexpect.EOF, pexpect.TIMEOUT]) # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序列印提示信息並退出. if (index != 0): print "ftp login failed" child.close(force=True) # 匹配到了密碼提示符,發送密碼 + 換行符給子程序. child.sendline(loginPassword) # 期望登錄成功后,提示符 "ftp>" 字元出現. index = child.expect( ['ftp>', 'Login incorrect', 'Service not available', pexpect.EOF, pexpect.TIMEOUT]) # 匹配到了 'ftp>',登錄成功. if (index == 0): print 'Congratulations! ftp login correct!' # 發送 'bin'+ 換行符給子程序,表示接下來使用二進位模式來傳輸文件. child.sendline("bin") print 'getting a file...' # 向子程序發送下載文件 rmall 的命令. child.sendline("get rmall") # 期望下載成功后,出現 'Transfer complete.*ftp>',其實下載成功后, # 會出現以下類似於以下的提示信息: # 200 PORT command successful. # 150 Opening data connection for rmall (548 bytes). # 226 Transfer complete. # 548 bytes received in 0.00019 seconds (2.8e+03 Kbytes/s) # 所以直接用正則表達式 '.*' 將 'Transfer complete' 和提示符 'ftp>' 之間的字元全省去. index = child.expect( ['Transfer complete.*ftp>', pexpect.EOF, pexpect.TIMEOUT] ) # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序列印提示信息並退出. if (index != 0): print "failed to get the file" child.close(force=True) # 匹配到了 'Transfer complete.*ftp>',表明下載文件成功,列印成功信息,並輸入 'bye',結束 ftp session. print 'successfully received the file' child.sendline("bye") # 用戶名或密碼不對,會先出現 'Login incorrect',然後仍會出現 'ftp>',但是 pexpect 是最小匹配,不是貪婪匹配, # 所以如果用戶名或密碼不對,會匹配到 'Login incorrect',而不是 'ftp>',然後程序列印提示信息並退出. elif (index == 1): print "You entered an invalid login name or password. Program quits!" child.close(force=True) # 匹配到了 'Service not available',一般表明 421 Service not available, remote server has # closed connection,程序列印提示信息並退出. # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序列印提示信息並退出. else: print "ftp login failed! index = " + index child.close(force=True) # 匹配到了 "(?i)Unknown host",表示 server 地址不對,程序列印提示信息並退出 elif index == 1 : print "ftp login failed, due to unknown host" child.close(force=True) # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序列印提示信息並退出 else: print "ftp login failed, due to TIMEOUT or EOF" child.close(force=True) |
註:
Congratulations! ftp login correct! getting a file... successfully received the file |
![]() ![]() |
例 2:記錄 log
本例實現了如下功能:運行一個命令,並將該命令的運行輸出結果記錄到 log 文件中 ./command.py [-a] [-c command] {logfilename} -c 後接的是要運行的命令的名字,默認是“ls -l”; logfilename 是記錄命令運行結果的 log 文件名,默認是“command.log”;指定 -a 表示命令的輸出結果會附加在 logfilename 后,如果 logfilename 之前已經存在的話。
#!/usr/bin/env python """ This run a user specified command and log its result. ./command.py [-a] [-c command] {logfilename} logfilename : This is the name of the log file. Default is command.log. -a : Append to log file. Default is to overwrite log file. -c : spawn command. Default is the command 'ls -l'. Example: This will execute the command 'pwd' and append to the log named my_session.log: ./command.py -a -c 'pwd' my_session.log """ import os, sys, getopt import traceback import pexpect # 如果程序中間出錯,列印提示信息後退出 def exit_with_usage(): print globals()['__doc__'] os._exit(1) def main(): ###################################################################### # Parse the options, arguments, get ready, etc. ###################################################################### try: optlist, args = getopt.getopt(sys.argv[1:], 'h?ac:', ['help','h','?']) # 如果指定的參數不是’ -a ’ , ‘ -h ’ , ‘ -c ’ , ‘ -? ’ , ‘ --help ’ , #‘ --h ’或’ --? ’時,會拋出 exception, # 這裡 catch 住,然後列印出 exception 的信息,並輸出 usage 提示信息. except Exception, e: print str(e) exit_with_usage() options = dict(optlist) # 最多只能指定一個 logfile,否則出錯. if len(args) > 1: exit_with_usage() # 如果指定的是 '-h','--h','-?','--?' 或 '--help',只輸出 usage 提示信息. if [elem for elem in options if elem in ['-h','--h','-?','--?','--help']]: print "Help:" exit_with_usage() # 獲取 logfile 的名字. if len(args) == 1: script_filename = args[0] else: # 如果用戶沒指定,默認 logfile 的名字是 command.log script_filename = "command.log" # 如果用戶指定了參數 -a,如果之前該 logfile 存在,那麼接下來的內容會附加在原先內容之後, # 如果之前沒有該 logfile,新建一個文件,並且接下來將內容寫入到該文件中. if '-a' in options: fout = open (script_filename, "ab") else: # 如果用戶沒指定參數 -a,默認按照用戶指定 logfile 文件名新建一個文件,然後將接下來將內容寫入到該文件中. fout = open (script_filename, "wb") # 如果用戶指定了 -c 參數,那麼運行用戶指定的命令. if '-c' in options: command = options['-c'] # 如果用戶沒有指定 -c 參數,那麼默認運行命令'ls – l' else: command = "ls -l" # logfile 文件的 title fout.write ('==========Log Tile: IBM developerWorks China==========\n') # 為接下來的運行命令生成一個 pexpect 的 spawn 類子程序的對象. p = pexpect.spawn(command) # 將之前 open 的 file 對象指定為 spawn 類子程序對象的 log 文件. p.logfile = fout # 命令運行完后,expect EOF 出現,這時會將 spawn 類子程序對象的輸出寫入到 log 文件. p.expect(pexpect.EOF) #open 完文件,使用完畢后,需關閉該文件. fout.close() return 0 if __name__ == "__main__": try: main() except SystemExit, e: raise e except Exception, e: print "ERROR" print str(e) traceback.print_exc() os._exit(1) |
[火星人 ] 探索 Pexpect,第 2 部分:Pexpect 的實例分析已經有1250次圍觀