zoukankan      html  css  js  c++  java
  • python pexpect模块的使用

    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_sendlogfile_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_readlogfile_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,但其最终结果是:内容为空。见下例:

    清单 3. log 内容为空的例子代码

    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
  • 相关阅读:
    Leetcode 70 Climbing Stairs 递推
    Leetcode 83 Remove Duplicates from Sorted List 链表
    Leetcode 328 Odd Even Linked List 链表
    Leetcode 206 Reverse Linked List 链表
    Spring中注解
    SpringMVC配置文件
    java设计模式----单例模式
    java设计模式----工厂方法模式
    java设计模式----装饰者模式
    java设计模式----观察者模式
  • 原文地址:https://www.cnblogs.com/linwenbin/p/10944152.html
Copyright © 2011-2022 走看看