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
