pexpect简介

pexpect是一个纯pyth实现的模块,pexpect可以某些交互式子进程进行交互从而代替手工操作。比如使用passwd命令需要人工输入密码,如果使用pexpect可以通过sendline自动化进行。
几个关键词掌握了就可以使用了。
spawn
expect
sendline
before
after

pexpect安装

使用pexpect之前需要安装一下

下载地址:

wget https://files.pythonhosted.org/packages/09/0e/75f0c093654988b8f17416afb80f7621bcf7d36bbd6afb4f823acdb4bcdc/pexpect-4.5.0.tar.gz

tar zxf pexpect-4.5.0.tar.gz

cd pexpect-4.5.0/

安装

python setup.py install

有时候只安装pexpect还可能报缺少ptyprocess 

下载地址:

https://pypi.python.org/pypi/ptyprocess#downloads 

下了个ptyprocess 一样解压,安装, 之后import pexpect没问题,可以用了。

pexpect的使用

基本使用流程

pexpect 的使用说来说去,就是围绕3个关键命令做操作:

  1. 首先用 spawn 来执行一个程序
  2. 然后用 expect 来等待指定的关键字,这个关键字是被执行的程序打印到标准输出上面的
  3. 最后当发现这个关键字以后,根据关键字用 send 方法来发送字符串给这个程序

第一步只需要做一次,但在程序中会不停的循环第二、三步来一步一步的完成整个工作。掌握这个概念之后 pexpect 的使用就很容易了。当然 pexpect 不会只有这 3 个方法,实际上还有很多外围的其他方法,我们一个一个来说明

spawn() - 执行程序

class spawn:def __init__(self,command,args=[],timeout=30,maxread=2000,\searchwindowsize=None, logfile=None, cwd=None, env=None)

spawn() 方法用来执行一个程序,它返回这个程序的操作句柄,以后可以通过操作这个句柄来对这个程序进行操作,比如:

process = pexpect.spawn('ftp sw-tftp')

上面 spawn() 中的字符串就是要执行的程序,这里我们打开一个到 sw-tftp 服务器的 ftp 连接。 spawn() 中的第一个元素就是要执行的命令,除此之外还可以指定一些其他参数,比如: pexpect.spawn('ftp sw-tftp', timeout=60) 就指定了超时时间,这些具体的会在后面讲解。

process 就是 spawn() 的程序操作句柄了,之后对这个程序的所有操作都是基于这个句柄的,所以它可以说是最重要的部分。尽量给它起个简短点的名字,不然后面的程序要多打不少字的。-

注意1: spawn() ,或者说 pexpect 并不会转译任何特殊字符 比如 | * 字符在Linux的shell中有特殊含义,但是在 pexpect 中不会转译它们,如果在 linux 系统中想使用这些符号的正确含义就必须加上 shell 来运行,这是很容易犯的一个错误。

正确的方式:

process = pexpect.spawn('/bin/bash –c "ls –l | grep LOG > log_list.txt"')
process.expect(pexpect.EOF)

spawn() 还有一种调用方式就是第一个参数是主程序,而下一个参数是主程序的参数,理解起来很麻烦?看看实际代码吧:

cmd = "ls –l | grep LOG > log_list.txt"
process = pexpect.spawn("/bin/bash", ["-c", cmd])
process.expect(pexpect.EOF)

这些代码和上面一个例子是相同的,是不是更清晰一些?

spawn 的选项包括下面这些:

cwd - 指定命令执行的目录

默认值: None 或者说 ./

cwd 用来指定命令发送的命令在哪个路径下执行,它一般是用在 send() 系列命令中,比如在 Linux 中,你想在 /etc 目录下执行 ls –l 命令,那么完全不需要用 sendline("cd /etc && ls -l") 这样的方式,而是用 sendline("ls –l", cwd="/etc") 就可以了。

env - 指定环境变量

默认值: None

指定环境变量的值,这个值是一个字典,如果你发送的命令要使用一些环境变量,那么可以在这里提供

ignore_sighup - 是否过滤 SIGHUP 信号

默认值: True

这个参数是 pexpect 3.1 开始引入的,在 3.1 之前(比如 pexpect 2.3),spawn 的子程序会过滤 SIGHUP 信号,也就是用 Ctrl+C 是不能终止子程序的,3.1的默认值也继承了这个行为,但是如果设置 ignore_sighup = False 就可以改变这个行为。

delaybeforesend - 字符发送延时

默认值: 0.05

这是一个隐藏参数用来设置发送字符串之前的延时。增加这个参数的最大理由是因为很多人碰见这样一个问题:

在 FTP 程序中登录时如果用脚本输入密码时会直接显示出来。这是基于一个一般人不可思议的事实:当 FTP 登录时,实际上服务器会先打印要求你输入密码的提示符,然后再发一个信号把回显功能取消,当人使用键盘输入的时候因为这个动作延时比较高所以不可能看到回显的密码,但脚本会在发现输入密码的提示符时立即发送,于是密码就会在关闭回显之前出现了。 Pexpect 为了解决这个问题在每次发送字符前默认等待 50 毫秒,如果你认为不必要的话就可以自己设置为 0 来取消这个行为。

spawn()类中的beforeafter属性:

expect 不断从读入缓冲区中匹配目标正则表达式,当匹配结束时 pexpect 的 before 成员中保存了缓冲区中匹配成功处之前的内容, pexpect 的 after 成员保存的是缓冲区中与目标正则表达式相匹配的内容。

child = pexpect.spawn('/bin/ls /')
child.expect (pexpect.EOF)
print child.before

以上代码就是打印在根目录下面执行ls命令后的输出内容

expect() - 关键字匹配

expect(self, pattern, timeout=-1, searchwindowsize=None)

当 spawn() 启动了一个程序并返回程序控制句柄后,就可以用 expect() 方法来等待指定的关键字了。它最后会返回 0 表示匹配到了所需的关键字,如果后面的匹配关键字是一个列表的话,就会返回一个数字表示匹配到了列表中第几个关键字,从 0 开始计算。

expect() 利用正则表达式来匹配所需的关键字。(正则表达式使用范围非常广,几乎所有语言都对它提供支持,如果不知道如何使用的话,可以参考我的另一份文档《正则表达式参考》)。

它的使用方式:

# pattern_list      正则表达式列表,表示要匹配这些内容
# timeout           不设置或者设置为-1的话,超时时间就采用self.timeout的值,默认是30秒。也可以自己设置。
# searchwindowsize  功能和 spawn 上的一样,但是!请注意这个但是!下面会实际说明
process.expect(pattern_list, timeout=-1, searchwindowsize=None)

在这里的 searchwindowsize 是在 expect() 方法中真正生效的,默认情况下是 None,也就是每从子进程中获取一个字符就做一次完整匹配,如果子进程的输出很多的话……性能会非常低。如果设置为其他的值,表示从子进程中读取到多少个字符才做一次匹配,这样会显著减少匹配的次数,增加性能。

经过测试,对于一个有 48100000 个字符的子进程,将 searchwindowsize 设置为 2000 时,完全处理完成需要 73.2730 秒;同样的子进程将这个参数设置为 None 则需要 1949.6259 秒,Oh, my Lady GAGA…… 完全是指数上升啊。

  • 最简单的匹配方式

    process.expect('[Nn]ame')
    

    上面的代码表示:匹配 process 这个句柄(代表 spawn 方法的例子中我们启动的 ftp 连接)中的 name 关键字,其中 n 不分大小写。

    上面的关键字一旦匹配,就会返回0表示匹配成功,但是如果一直匹配不到呢?默认是会一直等下去,但是如果设置了 timeout 的话就会超时。

  • 匹配一系列输出

    实际上, expect() 可以匹配一系列输出,通过检查匹配到的输出,我们可以做不同的事情。比如之前 spawn 的 ftp 连接,如果我们输入用户名之后有不同的情况,就可以通过监控这些不同情况来做不同的动作,比如:

    index = process.expect(['Permission Denied','Terminal type','ftp>',
    ])
    if index == 0:print "Permission denied at host, can't login."process.kill(0)
    elif index == 1:print "Login ok, set up terminal type…"process.sendline('vty100')process.expect("ftp>")
    elif index == 2:print "Login Ok, please send your command"process.interact()
    

    上面的代码中,expect 方法中的是一个列表,列表中的每个元素都是一个关键字的正则表达式,也就是说我们期待这 3 种情况之一,而 expect 返回一个顺序值来代表我匹配到了哪一个元素(也就是发生了哪种情况了),这个顺序值是从 0 开始计算的。

    当expect之后,下面的 if 语句就开始处理这 3 种情况了:

    1. 权限不足,这可能是 ftp 服务器出现问题,或者没有这个帐号,或者其他什么情况,反正只要发现这种情况的话,我们就给用户提示一下,然后杀掉这个进程
    2. 登陆成功,但还要用户指定终端模式才能真正使用,所以我们在代码中指定了 vty100 这种模式,然后看是不是能真正使用了
    3. 还是登陆成功了,而且还可以直接输入命令操作 ftp 服务器了,于是我们提示用户,然后把操作权限交给用户

    另外有一种特殊情况,如果同时有2个被匹配到,那么怎么办?简单来说就是这样:

    1. 原始流中,第一个被关键字匹配到的内容会被使用
    2. 匹配关键字列表中,最左边的会被使用

    给个例子:

    # 如果流里面的内容是 "hello world"
    index = process.expect(["hi", "hello", "hello world"])
    

    返回的值是 1,也就是 'hello' 被匹配到了,哪怕真正最好的匹配是 "hello world" 但因为放在后面所以仍然无效。

使用技巧

  1. 如果要检查或者匹配 expect.EOF 和 expect.TIMEOUT 这两种情形,那么必须将它们放进匹配列表里面去,这样可以通过检查返回的数字来处理它们。如果没放进列表的话,就会发生 EOF 或者 TIMEOUT 错误,程序就会中途停止了

  2. 匹配规则中有些特殊语法,比如下面的规则中前 2 个匹配都是大小写无关的,关键就是这个 (?i) 匹配规则,它相当于 re.IGNORE 或者 re.I 这个关键字,因为毕竟不是真正的正则表达式引擎,所以 pexpect 使用这样特殊语法:

    child.expect(['(?i)etc', '(?i)readme', pexpect.EOF, pexpect.TIMEOUT])
    

expect_exact() - 精确匹配

它的使用和 expect() 是一样的,唯一不同的就是它的匹配列表中不再使用正则表达式。

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

send() - 发送关键字

send() 作为3个关键操作之一,用来向程序发送指定的字符串,它的使用没什么特殊的地方,比如:

process.expect("ftp>")
process.send("by\n")

这个方法会返回发送字符的数量。

sendline() - 发送带回车符的字符串

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

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

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

interact() - 将控制权交给用户

interact() 表示将控制权限交给用户(或者说标准输入)。一般情况下 pexpect 会接管所有的输入和输出,但有的时候还是希望用户介入,或者仅仅是为了完成一部分工作的时候, interact() 就很有用了。

比如:

  1. 登陆 ftp 服务器的时候,在输入用户密码阶段希望用户手工输入密码,然后脚本完成剩余工作时(将用户密码写在脚本中可不安全)
  2. 只希望完成登陆工作,比如要 ssh 连接到一台远方的服务器,但中间要经过好几跳,用手工输入实在太麻烦,所以就用脚本先跳到目的服务器上,然后再把控制权限还给用户做操作。

使用方法:

# escape_character 就是当用户输出这里指定的字符以后表示自己的操作完成了,将控制权重新交给 pexpect
process.interact(escape_character='\x1d', input_filter=None, output_filter= None)

详细来说,这个方法将控制权交给用户(或者说用户操作的键盘),然后简单的将标准输出、标准错误输出和标准输入绑定到系统上来。

通过设置 escape_character 的值,可以定义返回码,默认是 <kbd>ctrl+]</kbd> 或者说 <kbd>^]</kbd>,当输入了返回码以后,脚本会将控制权从用户那里重新拿回来,然后继续向下执行。

close() - 停止应用程序

如果想中途关闭子程序,那么可以用 close 来完成,调用这个方法后会返回这个程序的返回值。

如果设置 force=True 会强行关闭这个程序,大概的过程就是先发送 SIGHUP 和 SIGINT 信号,如果都无效的话就发 SIGKILL 信号,反正不管怎么样都会保证这个程序被关闭掉。

多次调用这个方法是允许的,但是不保证每次都能返回正确的返回值。尽量不要这么做,如果想保证程序被关闭的话只要设置force的值就可以了。

扫一扫加入大数据公众号和技术交流群,了解更多大数据技术,还有免费资料等你哦

扫一扫加入大数据公众号和技术交流群,了解更多大数据技术,还有免费资料等你哦

扫一扫加入大数据公众号和技术交流群,了解更多大数据技术,还有免费资料等你哦

python的pexpect模块的应用相关推荐

  1. python pexpect telnet_使用python的pexpect模块,实现远程免密登录的示例

    说明 当我们需要用脚本实现,远程登录或者远程操作的时候,都要去解决如何自动输入密码的问题,一般来说有3种实现方式: 1).配置公钥私钥 2).使用shell下的命令,expect 3).使用pytho ...

  2. python sendline_python Pexpect模块的使用

    Pexpect简介 在讲解Pexpect之前,我们需要先了解一下Expect这个脚本语言,它是由TCL语言实现的,主要用于人机交互式对话的自动化控制,可以用来完成ssh.ftp.telnet等命令行程 ...

  3. python代替shell脚本_自动化shell脚本except与python的pexpect模块

    expect脚本 expect是什么 expect是一个免费的编程工具,用来实现自动的交互式任务,而无需人为干预.说白了,expect就是一套用来实现自动交互功能的软件. 在实际工作中,我们运行命令. ...

  4. Python的Pexpect模块详解

    对于存在交互过程的远程访问,如ssh, ftp, mencoder, passwd等,通过Pexpect模块可以根据应用的输出控制交互过程,从而提高容错性. Pexpect模块首先通过生成子应用以代理 ...

  5. python pexpect_python pexpect模块的使用

    Pexpect 是一个用来启动子程序并对其进行自动控制的 Python 模块. Pexpect 可以用来和像 ssh.ftp.passwd.telnet 等命令行程序进行自动交互. pexpect模块 ...

  6. Python Pexpect 模块使用说明

    背景介绍 Expect 程序主要用于人机对话的模拟,就是那种系统提问,人来回答 yes/no ,或者账号登录输入用户名和密码等等的情况.因为这种情况特别多而且繁琐,所以很多语言都有各种自己的实现.最初 ...

  7. Pexpect 模块使用说明

    背景介绍 Expect 程序主要用于人机对话的模拟,就是那种系统提问,人来回答 yes/no ,或者账号登录输入用户名和密码等等的情况.因为这种情况特别多而且繁琐,所以很多语言都有各种自己的实现.最初 ...

  8. python pexpect模块详解_python pexpect模块

    Pexpect模块简介: Pexpect 是 Don Libes 的 Expect 语言的一个 Python 实现,是一个用来启动子程序,并使用正则表达式对程序输出做出特定响应,以此实现与其自动交互的 ...

  9. python 解析模块脚本_Python pexpect模块及shell脚本except原理解析

    expect脚本 expect是什么 expect是一个免费的编程工具,用来实现自动的交互式任务,而无需人为干预.说白了,expect就是一套用来实现自动交互功能的软件. 在实际工作中,我们运行命令. ...

最新文章

  1. Java虚拟机性能监控与调优实战
  2. vw 前端_一行css代码轻松实现前端响应式布局(vw+rem)
  3. 来学习几个简单的Hive函数啦
  4. HBase 学习(三) JavaAPI的使用
  5. Bailian4106 出现两次的字符-Characters Appearing twice【计数统计】
  6. php和java环境整合
  7. php相册照片批量修改,php如何实现批量修改文件名称
  8. php中清除文本框,php如何清除文本框
  9. 用python编程解决鸡兔同笼问题
  10. 【python爬虫自学笔记】-----爬取简书网站首页文章标题与链接
  11. linux postfix 虚拟,postfix虚拟别名域的配置
  12. acm比赛经验(转)
  13. 深入理解IOC和DI的区别
  14. E站账号cookie分享_不用输入密码无风险?扫描二维码登录QQ账号也不安全!
  15. sumproduct函数的深入理解
  16. 个人APP盈利之道:内容为王 学会适当放弃
  17. 优思学院|六西格玛管理6个最常用的工具
  18. CSS 实现斑马条纹
  19. 战舰V3适配oneos系列02:添加串口驱动
  20. antd Table 实现 表格行固定

热门文章

  1. mt全国人气榜店铺榜2.0
  2. 全面屏iphone 适配
  3. android 把一个Activity窗口化的实现
  4. 【每日最爱一句】2013.06.25
  5. 科技查新报告撰写规范有哪些?
  6. 如何使用WordPress制作落地页
  7. (ssl1455)电子老鼠闯迷宫
  8. golang 定义二维数组的长度
  9. 2022-2028全球及中国微电子医用植入物行业研究及十四五规划分析报告
  10. 山东省海洋与渔业厅灾备设备项目