Pexpect 是一個用來啟動子程序並對其進行自動控制的 Python 模塊。 Pexpect 可以用來和像 ssh、ftp、passwd、telnet 等命令行程序進行自動交互。
pexpect模塊在windows功能有限,在linux系統中用得比較多。
pexpect有兩個類或方法用得比較多,一個是pexpect.run() 和pexpect.spawn()
pexpect.run()類似於os.system(),但pexpect.run()會返回執行結果
pexpect.spawn()這個類的功能比較強大,可以用於以下功能:
1. ftp 的使用(注:spawn、expect 和 sendline 的使用)
2. 記錄 log(注:logfile、
logfile_send
和
logfile_read
的使用)
3. ssh 的使用
4. pxssh 的使用
5. telnet 的使用(注:interact 的使用)
使用注意事項:
1.不能使用linux命令中的管道符重定向符等
2.spawn里面的參數列表里面不要使用變量名
以下是實例:
1.pexpect連接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)
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)
注:
運行:./command.py -a -c who cmd.log
運行結束后,cmd.log 的內容為: IBM developerWorks China Root :0 2009-05-12 22:40 Root pts/1 2009-05-12 22:40 (:0.0) Root pts/2 2009-07-05 18:55 (9.77.180.94) |
logfile
:
只能通過 spawn 類的構造函數指定。在 spawn 類的構造函數通過參數指定 logfile 時,表示開啟或關閉 logging 。所有的子程序的 input 和 output 都會被 copy 到指定的 logfile 中。設置 logfile 為 None 表示停止 logging,默認就是停止 logging 。設置 logfile 為 sys.stdout,會將所有東西 echo 到標准輸出。
logfile_read
和
logfile_send
:
logfile_read:只用來記錄 python 主程序接收到 child 子程序的輸出,有的時候你不想看到寫給 child 的所有東西,只希望看到 child 發回來的東西。 logfile_send:只用來記錄 python 主程序發送給 child 子程序的輸入 logfile、logfile_read 和 logfile_send 何時被寫入呢? logfile、logfile_read 和 logfile_send 會在每次寫 write 和 send 操作后被 flush 。
-
調用 send 后,才會往 logfile 和 logfile_send 中寫入,sendline/sendcontrol/sendoff/write/writeline 最終都會調用 send,所以 sendline 后 logfile 中一定有內容了,只要此時 logfile 沒有被 close 。
調用 read_nonblocking 后,才會往 logfile 和 logfile_read 中寫入,expect_loop 會調用 read_nonblocking,而 expect_exact 和 expect_list 都會調用 expect_loop,expect 會調用 expect_list,所以 expect 后 logfile 中一定有內容了,只要此時 logfile 沒有被 close 。
如果調用的函數最終都沒有調用 send 或 read_nonblocking,那么 logfile 雖然被分配指定了一個 file,但其最終結果是:內容為空。見下例:
import pexpect p = pexpect.spawn( ‘ ls -l ’ ) fout = open ('log.txt', "w") p.logfile = fout fout.close() |
運行該腳本后,你會發現其實 log.txt 是空的,沒有記錄 ls -l 命令的內容,原因是沒有調用 send 或 read_nonblocking,真正的內容沒有被 flush 到 log 中。如果在 fout.close() 之前加上 p.expect(pexpect.EOF),log.txt 才會有 ls -l 命令的內容。
3.ssh的使用
#!/usr/bin/env python """ This runs a command on a remote host using SSH. At the prompts enter hostname, user, password and the command. """ import pexpect import getpass, os #user: ssh 主機的用戶名 #host:ssh 主機的域名 #password:ssh 主機的密碼 #command:即將在遠端 ssh 主機上運行的命令 def ssh_command (user, host, password, command): """ This runs a command on the remote host. This could also be done with the pxssh class, but this demonstrates what that class does at a simpler level. This returns a pexpect.spawn object. This handles the case when you try to connect to a new host and ssh asks you if you want to accept the public key fingerprint and continue connecting. """ ssh_newkey = 'Are you sure you want to continue connecting' # 為 ssh 命令生成一個 spawn 類的子程序對象. child = pexpect.spawn('ssh -l %s %s %s'%(user, host, command)) i = child.expect([pexpect.TIMEOUT, ssh_newkey, 'password: ']) # 如果登錄超時,打印出錯信息,並退出. if i == 0: # Timeout print 'ERROR!' print 'SSH could not login. Here is what SSH said:' print child.before, child.after return None # 如果 ssh 沒有 public key,接受它. if i == 1: # SSH does not have the public key. Just accept it. child.sendline ('yes') child.expect ('password: ') i = child.expect([pexpect.TIMEOUT, 'password: ']) if i == 0: # Timeout print 'ERROR!' print 'SSH could not login. Here is what SSH said:' print child.before, child.after return None # 輸入密碼. child.sendline(password) return child def main (): # 獲得用戶指定 ssh 主機域名. host = raw_input('Hostname: ') # 獲得用戶指定 ssh 主機用戶名. user = raw_input('User: ') # 獲得用戶指定 ssh 主機密碼. password = getpass.getpass() # 獲得用戶指定 ssh 主機上即將運行的命令. command = raw_input('Enter the command: ') child = ssh_command (user, host, password, command) # 匹配 pexpect.EOF child.expect(pexpect.EOF) # 輸出命令結果. print child.before if __name__ == '__main__': try: main() except Exception, e: print str(e) traceback.print_exc() os._exit(1)
4. pxssh的使用
#!/usr/bin/env python import pxssh import getpass try: # 調用構造函數,創建一個 pxssh 類的對象. s = pxssh.pxssh() # 獲得用戶指定 ssh 主機域名. hostname = raw_input('hostname: ') # 獲得用戶指定 ssh 主機用戶名. username = raw_input('username: ') # 獲得用戶指定 ssh 主機密碼. password = getpass.getpass('password: ') # 利用 pxssh 類的 login 方法進行 ssh 登錄,原始 prompt 為'$' , '#'或'>' s.login (hostname, username, password, original_prompt='[$#>]') # 發送命令 'uptime' s.sendline ('uptime') # 匹配 prompt s.prompt() # 將 prompt 前所有內容打印出,即命令 'uptime' 的執行結果. print s.before # 發送命令 ' ls -l ' s.sendline ('ls -l') # 匹配 prompt s.prompt() # 將 prompt 前所有內容打印出,即命令 ' ls -l ' 的執行結果. print s.before # 退出 ssh session s.logout() except pxssh.ExceptionPxssh, e: print "pxssh failed on login." print str(e)
5.telnet 的使用
#!/usr/bin/env python import pexpect # 即將 telnet 所要登錄的遠程主機的域名 ipAddress = 'develperWorks.ibm.com' # 登錄用戶名 loginName = 'root' # 用戶名密碼 loginPassword = 'passw0rd' # 提示符,可能是’ $ ’ , ‘ # ’或’ > ’ loginprompt = '[$#>]' # 拼湊 telnet 命令 cmd = 'telnet ' + ipAddress # 為 telnet 生成 spawn 類子程序 child = pexpect.spawn(cmd) # 期待'login'字符串出現,從而接下來可以輸入用戶名 index = child.expect(["login", "(?i)Unknown host", pexpect.EOF, pexpect.TIMEOUT]) if ( index == 0 ): # 匹配'login'字符串成功,輸入用戶名. child.sendline(loginName) # 期待 "[pP]assword" 出現. index = child.expect(["[pP]assword", pexpect.EOF, pexpect.TIMEOUT]) # 匹配 "[pP]assword" 字符串成功,輸入密碼. child.sendline(loginPassword) # 期待提示符出現. child.expect(loginprompt) if (index == 0): # 匹配提示符成功,輸入執行命令 'ls -l' child.sendline('ls -l') # 立馬匹配 'ls -l',目的是為了清除剛剛被 echo 回顯的命令. child.expect('ls -l') # 期待提示符出現. child.expect(loginprompt) # 將 'ls -l' 的命令結果輸出. print child.before print "Script recording started. Type ^] (ASCII 29) to escape from the script shell." # 將 telnet 子程序的執行權交給用戶. child.interact() print 'Left interactve mode.' else: # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序打印提示信息並退出. print "telnet login failed, due to TIMEOUT or EOF" child.close(force=True) else: # 匹配到了 pexpect.EOF 或 pexpect.TIMEOUT,表示超時或者 EOF,程序打印提示信息並退出. print "telnet login failed, due to TIMEOUT or EOF" child.close(force=True)
更詳細的情況請查看:
https://www.cnblogs.com/mmdln/p/9006274.html