Python-pexpect

Expect 程序主要用于人机对话的模拟

  1. 运行程序
  2. 程序要求人的判断和输入
  3. Expect 通过关键字匹配
  4. 根据关键字向程序发送符合的字符串

1、使用流程

  1. 首先用 spawn 来执行一个程序

  2. 使用 expect 来等待指定的关键字,这个关键字是被执行的程序打印到标准输出上面的

  3. 最后当发现这个关键字以后,根据关键字用 send 方法来发送字符串给这个程序

  4. 例子:

ipl = open(iplist)
for i in ipl:
    ip = i.split()[0].replace('\n','')
    child = pexpect.spawn('ssh %s' %ip)
    try:
        tag = child.expect(['Password:'])
        print (ip,"tag:",str(tag))
        if tag == 0:
            child.sendline(oldpwd)
            print(oldpwd)

            tag1 = child.expect(['New password:'])
            print(ip, "tag1:", str(tag1))
        else:
            child.close()
            print(ip+"fault")
    except pexpect.EOF:
        print(ip,"EOF")
    except pexpect.TIMEOUT:
        print(ip,"TIMEOUT")f

2、spawn类:

spawn()第一个参数command的使用,command参数不支持直接使用管道,通配符,标志输入,输出,错误重定向,如要使用就必须配合args参数

   class spawn(SpawnBase):
       '''This is the main class interface for Pexpect. Use this class to start
       and control child applications. '''
   
       # This is purely informational now - changing it has no effect
       use_native_pty_fork = use_native_pty_fork
   
       def __init__(self, command, args=[], timeout=30, maxread=2000,
                    searchwindowsize=None, logfile=None, cwd=None, env=None,
                    ignore_sighup=False, echo=True, preexec_fn=None,
                    encoding=None, codec_errors='strict', dimensions=None,
                    use_poll=False):

例子:

child = pexpect.spawn('ls -l', logfile=sys.stdout, cwd = '/home')
child.expect(pexpect.EOF)

其他参数

# timeout参数:设置超时时间,单位为秒
# maxread参数:从TTY读取信息最大缓冲区
# logfile=None:指定日志文件,可指定为sys.stdout
# cwd=None:指定命令运行的目录,默认值 None 或者说 ./
# env=None:命令运行时的环境变量
# encoding=None:命令运行时的编码
# codec_errors=‘strict’:编码转换时的选项

3、expect()方法

expect() 方法可以用来等待指定的关键字。它最后会返回 0 表示匹配到了所需的关键字,如果后面的匹配关键字是一个列表的话,就会返回一个数字表示匹配到了列表中第几个关键字,从 0 开始计算。
expect() 利用正则表达式来匹配所需的关键字。

child.expect(pattern_list, timeout=-1, searchwindowsize=None)
# pattern_list   正则表达式列表,表示要匹配这些内容
# timeout        不设置或者设置为-1的话,超时时间就采用self.timeout的值,默认是30秒。也可以自己设置。
# searchwindowsize  功能和 spawn 上的一样,默认情况下是 None。

before/after/match:获取程序运行输出
当 expect() 过程匹配到关键字(或者说正则表达式)之后,系统会自动给3个变量赋值,分别是 before, after 和 match

before - 保存了到匹配到关键字为止,缓存里面已有的所有数据。也就是说如果缓存里缓存了 100 个字符的时候终于匹配到了关键字,那么 before 就是除了匹配到的关键字之外的所有字符
after - 保存匹配到的关键字,比如你在 expect 里面使用了正则表达式,那么表达式匹配到的所有字符都在 after 里面
match - 保存的是匹配到的正则表达式的实例,和上面的 after 相比一个是匹配到的字符串,一个是匹配到的正则表达式实例
如果 expect() 过程中发生错误,那么 before 保存到目前位置缓存里的所有数据, after 和 match 都是 None

[root@zabbix test]# cat ./test.py
#!/usr/local/python
# -*- coding: utf-8 -*-

import pexpect
import sys,os

child = pexpect.spawn('ls -l /')
child.expect('run')

print("=="*20)
print(child.before)
print("=="*20)
print(child.match)
print("=="*20)
print(child.after)

执行后

[root@zabbix test]# python ./test.py
========================================
total 32
lrwxrwxrwx.   1 root root     7 Jul  9 21:43 bin -> usr/bin
dr-xr-xr-x.   5 root root  4096 Jul  9 22:09 boot
drwxr-xr-x   19 root root  3260 Dec 15 22:55 dev
drwxr-xr-x. 158 root root 12288 Dec  4 22:28 etc
drwxr-xr-x.  10 root root   171 Dec  4 22:28 home
lrwxrwxrwx.   1 root root     7 Jul  9 21:43 lib -> usr/lib
lrwxrwxrwx.   1 root root     9 Jul  9 21:43 lib64 -> usr/lib64
drwxr-xr-x.   2 root root     6 Apr 10  2018 media
drwxr-xr-x.   3 root root    18 Jul  9 22:04 mnt
drwxr-xr-x    2 root root    90 Aug 14 23:49 nginx
drwxr-xr-x.   4 root root    34 Jul 15 07:17 opt
dr-xr-xr-x  225 root root     0 Dec 15 22:55 proc
drwxr-xr-x    4 root root   145 Jul 17 19:54 prometheus
dr-xr-x---.  12 root root  4096 Dec 15 23:10 root
drwxr-xr-x   47 root root  1340 Dec 15 23:00
========================================
<_sre.SRE_Match object at 0x7f921ac12780>
========================================
run

expect如果没有匹配到任何字符则抛出异常:

pexpect.exceptions.EOF: End Of File (EOF). Exception style platform.
<pexpect.pty_spawn.spawn object at 0x7f34a4df9810>
command: /usr/bin/ls
args: ['/usr/bin/ls', '-l', './']
buffer (last 100 chars): ''
before (last 100 chars): 'total 4\r\n-rw-r--r-- 1 root root 188 Dec 15 23:02 tes
after: <class 'pexpect.exceptions.EOF'>
match: None
match_index: None
exitstatus: 0
flag_eof: True
pid: 2699
child_fd: 5
closed: False
timeout: 30
delimiter: <class 'pexpect.exceptions.EOF'>
logfile: None
logfile_read: None
logfile_send: None
maxread: 2000
ignorecase: False
searchwindowsize: None
delaybeforesend: 0.05
delayafterclose: 0.1
delayafterterminate: 0.1
searcher: searcher_re:
    0: re.compile('run')

可以匹配异常,让异常不在终端显示,从而程序不退出运行:

child = pexpect.spawn('ls -l ./')
child.expect(pexpect.EOF)			# 如果将此行代码打印的话会输出0

4、sendline()方法

sendline() - 发送带回车符的字符串
sendline() 和 send() 唯一的区别就是在发送的字符串后面加上了回车换行符,这也使它们用在了不同的地方:

  1. 只需要发送字符就可以的话用send()
  2. 如果发送字符后还要回车的话,就用 sendline()

它也会返回发送的字符数量

5、其他发送方法

send() :发送关键字
send() 作为3个关键操作之一,用来向程序发送指定的字符串,末尾不带回车换行符

sendcontrol():发送控制信号
sendcontrol() 向子程序发送控制字符,比如 ctrl+C 或者 ctrl+D 之类的,比如你要向子程序发送 ctrl+G,那么就这样写:child.sendcontrol('g')

sendeof() :发送 EOF 信号
向子程序发送 End Of File 信号,一般用于确认上一次发送内容缓冲结束

sendintr():发送终止信号
向子程序发送 SIGINT 信号,相当于 Linux 中的 kill 2 ,它会直接终止掉子进程

write():发送字符串
类似于send()命令,只不过不会返回发送的字符数。

writelines():发送包含字符串的列表
类似于 write() 命令,只不过接受的是一个字符串列表, writelines() 会向子程序一条一条的发送列表中的元素,但是不会自动在每个元素的最后加上回车换行符。
与 write() 相似的是,这个方法也不会返回发送的字符数量。

6、其他获取结果的方法

expect_exact():精确匹配
它的使用和 expect() 是一样的,唯一不同的就是它的匹配列表中不再使用正则表达式。
从性能上来说 expect_exact() 要更好一些,因为即使你没有使用正则表达式而只是简单的用了几个字符 expect() 也会先将它们转换成正则表达式模式然后再搜索,但 expect_exact() 不会,而且也不会把一些特殊符号转换掉。

expect_list():预转换匹配
使用方式和 expect() 一样,唯一不同的就是匹配列表只用已编译正则表达式和EOF, TIMEOUT;提高匹配速度;expect()方法是通过它工作的。
expect() 稍微有点笨,每调用一次它都会将内部的正则表达式转换一次(当然也有其他办法避免),如果你是在以后循环中调用 expect() 的话,多余的转换动作就会降低性能,在这种情况下建议用 expect_list() 来代替。

expect_loop():
用于从标准输入中获取内容,loop这个词代表它会进入一个循环,必须要从标准输入中获取到关键字才会往下继续执行。

read():返回剩下的所有内容

read():返回剩下的所有内容

readlines():返回列表模式的所有输出****

posted @ 2022-12-16 16:44  Fanslyx  阅读(153)  评论(0)    收藏  举报