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========== ') # 为接下来的运行命令生成一个 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