探索 Pexpect,第 2 部分:Pexpect 的實例分析

火星人 @ 2014-03-12 , reply:0
←手機掃碼閱讀

  
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 的使用(註:spawn、expect 和 sendline 的使用)
  • 例 2:記錄 log(註:logfile、logfile_send和logfile_read的使用)
  • 例 3:ssh 的使用
  • 例 4:pxssh 的使用
  • 例 5:telnet 的使用(註:interact 的使用)
  • pexpect 使用 tips
    • 調試 pexpect 程序的 tips
    • pexpect 不會解釋 shell 中的元字元
    • EOF 異常和 TIMEOUT 異常
    • 使用 run() 來替代某些的 spawn 的使用
    • expect_exact() 的使用
    • expect() 中正則表達式的使用 tips
    • isalive() 的使用 tips
    • delaybeforesend 的使用 tips




例 1:ftp 的使用

本例實現了如下功能:ftp 登錄到 develperWorks.ibm.com 主機上,並用二進位傳輸模式下載一個名叫 rmall的文件。


清單 1. ftp 的例子代碼
#!/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

  • 本例 expect 函數中的 pattern 使用了 List,並包含了 pexpect.EOF和pexpect.TIMEOUT,這樣出現了超時或者 EOF,不會拋出 expection 。(關於 expect() 函數的具體使用,請參閱參考資料)
  • 如果程序運行中間出現了錯誤,如用戶名密碼錯誤,超時或者 EOF,遠程 server 連接不上,都會使用 c hild.close(force=True) 關掉 ftp 子程序。調用 close 可以用來關閉與子程序的 connection 連接,如果你不僅想關閉與子程序的連接,還想確保子程序是真的被 terminate 終止了,設置參數 force=True,其最終會調用 c hild.kill(signal.SIGKILL) 來殺掉子程序。




例 2:記錄 log

本例實現了如下功能:運行一個命令,並將該命令的運行輸出結果記錄到 log 文件中 ./command.py [-a] [-c command] {logfilename} -c 後接的是要運行的命令的名字,默認是“ls -l”; logfilename 是記錄命令運行結果的 log 文件名,默認是“command.log”;指定 -a 表示命令的輸出結果會附加在 logfilename 后,如果 logfilename 之前已經存在的話。


清單 2. 記錄 log 的例子代碼
#!/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)





[火星人 via ] 探索 Pexpect,第 2 部分:Pexpect 的實例分析已經有369次圍觀

http://www.coctec.com/docs/linux/show-post-68764.html