一、引子

Pexpect程序主要用于人机对话的模拟,就是那种系统提问,人来回答yes/no,或者账号登陆输入用户名和密码等等的情况。因为这种情况特别多而且繁琐,所以很多语言都有各种自己的实现。最初的第一个 Expect 是由 TCL 语言实现的,所以后来的 Expect 都大致参考了最初的用法和流程,整体来说大致的流程包括:

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

TCL 语言实现的 Expect 功能非常强大,我曾经用它实现了防火墙设备的完整测试平台。也因为它使用方便、范围广,几乎所有脚本语言都实现了各种各样的类似与Expect的功能,它们叫法虽然不同,但原理都相差不大

pexpect 是 Python 语言的类 Expect 实现。从我的角度来看,它在功能上与 TCL 语言的实现还是有一些差距,比如没有buffer_full 事件、比如没有 expect before/after 事件等,但用来做一般的应用还是足够了。

二、基本使用流程

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

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

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

三、API

spawn()-执行程序

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

process = pexpect.spawn('df -h')

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

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

正确的方式:

import pexpect

process = pexpect.spawn('df -h')
print(process.expect(pexpect.EOF)) # 打印index

timeout - 超时时间

默认值:30(单位:秒)

指定程序的默认超时时间。程序被启动之后会输出,我们也会在脚本中检查出中的关键字是否是以知并处理的,如果指定时间内没找到程序就会出错返回。

maxread - 缓存设置

默认值:2000(单位:字符)

指定一次性试着从命令输出中读多少数据。如果设置的数字比较大,那么从 TTY 中读取数据的次数就会少一些。

设置为 1 表示关闭读缓存。

设置更大的数值会提高读取大量数据的性能,但会浪费更多的内存。这个值的设置与 searchwindowsize 合作会提供更多功能。

缓存的大小并不会影响获取的内容,也就是说如果一个命令输出超过2000个字符以后,先前缓存的字符不会丢失掉,而是放到其他地方去,当你用 self.before (这里 self 代表 spawn 的实例)还是可以取到完整的输出的。

searchwindowsize - 模式匹配阀值

默认值: None

searchwindowsize 参数是与 maxread 参数一起合作使用的,它的功能比较微妙,但可以显著减少缓存中有很多字符时的匹配时间。

默认情况下, expect() 匹配指定的关键字都是这样:每次缓存中取得一个字符时就会对整个缓存中的所有内容匹配一次正则表达式,你可以想像如果程序的返回特别多的时候,性能会多么的低。

设置 searchwindowsize 的值表示一次性收到多少个字符之后才匹配一次表达式,比如现在有一条命令会出现大量的输出,但匹配关键字是标准的 FTP 提示符 ftp> ,显然要匹配的字符只有 5 个(包括空格),但是默认情况下每当 expect 获得一个新字符就从头匹配一次这几个字符,如果缓存中已经有了 1W 个字符,一次一次的从里面匹配是非常消耗资源的,这个时候就可以设置 searchwindowsize=10, 这样 expect 就只会从最新的(最后获取的) 10 个字符中匹配关键字了,如果设置的值比较合适的话会显著提升性能。不用担心缓存中的字符是否会被丢弃,不管有多少输出,只要不超时就总会得到所有字符的,这个参数的设置仅仅影响匹配的行为。

这个参数一般在 expect() 命令中设置, pexpect 2.x 版本似乎有一个 bug ,在 spawn 中设置是不生效的。

logfile - 运行输出控制

默认值: None

当给 logfile 参数指定了一个文件句柄时,所有从标准输入和标准输出获得的内容都会写入这个文件中(注意这个写入是 copy 方式的),如果指定了文件句柄,那么每次向程序发送指令(process.send)都会刷新这个文件(flush)。

这里有一个很重要的技巧:如果你想看到spawn过程中的输出,那么可以将这些输出写入到 sys.stdout 里去,比如:

process = pexpect.spawn("ftp sw-tftp", logfile=sys.stdout)

用这样的方式可以看到整个程序执行期间的输入和输出,很适合调试。

还有一个例子:

process = pexpect.spawn("ftp sw-tftp")
logFileId = open("logfile.txt", 'w')
process.logfile = logFileId

注意: logfile.txt 文件里,既包含了程序运行时的输出,也包含了 spawn 向程序发送的内容,有的时候你也许不希望这样,因为某些内容出现了2次,那么还有 2 个很重要的 logfile 关联参数:

logfile_read - 获取标准输出的内容

默认值: None

记录执行程序中返回的所有内容,也就是去掉你发出去的命令,而仅仅只包括命令结果的部分:


process.logfile_read = sys.stdout

上面的语句会在屏幕上打印程序执行过程中的所有输出,但是一般不包含你向程序发送的命令,不过大部分程序都有回显机制,比如发命令的时候设备不光接收到命令字符串,还会反向在你的终端上把字符串显示出来让你明白哪些字符被输入了,这种时候也是会被这个方法读到的。只有那些不会回显的情况 logfile_read 才会拿不到,比如输入密码的时候。

logfile_send - 获取发送的内容

默认值: None

记录向执行程序发送的所有内容


process.logfile_send = sys.stdout

四、pexpect实现ssh操作

# -*- coding: utf-8 -*-
#!/usr/bin/python import pexpect def login_ssh_password(port,user,host,passwd):
'''函数:用于实现pexpect实现ssh的自动化用户密码登录'''
if port and user and host and passwd:
ssh = pexpect.spawn('ssh -p %s %s@%s' % (port,user, host))
i = ssh.expect(['password:', 'continue connecting (yes/no)?'], timeout=5)
if i == 0 :
ssh.sendline(passwd)
elif i == 1:
ssh.sendline('yes\n') # 交互认证
ssh.expect('password: ')
ssh.sendline(passwd)
index = ssh.expect (["#", pexpect.EOF, pexpect.TIMEOUT])
if index == 0:
print("logging in as root!")
ssh.interact()
elif index == 1:
print("logging process exit!")
elif index == 2:
print("logging timeout exit")
else:
print("Parameter error!") def login_ssh_key(keyfile,user,host,port):
'''函数:用于实现pexpect实现ssh的自动化密钥登录''' if port and user and host and keyfile:
ssh = pexpect.spawn('ssh -i %s -p %s %s@%s' % (keyfile,port,user, host))
i = ssh.expect( [pexpect.TIMEOUT,'continue connecting (yes/no)?'], timeout=2)
if i == 1:
ssh.sendline('yes\n')
index = ssh.expect (["#", pexpect.EOF, pexpect.TIMEOUT])
else:
index = ssh.expect (["#", pexpect.EOF, pexpect.TIMEOUT])
if index == 0:
print("logging in as root!")
ssh.interact()
elif index == 1:
print("logging process exit!")
elif index == 2:
print("logging timeout exit")
else:
print("Parameter error!") def main():
'''主函数:实现两种方式分别的登录'''
login_ssh_password('22','root','10.211.55.12','admin')
# login_ssh_key(keyfile="/tmp/id_rsa",port='22',user='root',host='192.168.1.101') if __name__ == "__main__":
main()

Python之pexpect详解的更多相关文章

  1. python的pexpect详解

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

  2. Python 字符串方法详解

    Python 字符串方法详解 本文最初发表于赖勇浩(恋花蝶)的博客(http://blog.csdn.net/lanphaday),如蒙转载,敬请保留全文完整,切勿去除本声明和作者信息.        ...

  3. python time模块详解

    python time模块详解 转自:http://blog.csdn.net/kiki113/article/details/4033017 python 的内嵌time模板翻译及说明  一.简介 ...

  4. Python中dict详解

    from:http://www.cnblogs.com/yangyongzhi/archive/2012/09/17/2688326.html Python中dict详解 python3.0以上,pr ...

  5. Python开发技术详解(视频+源码+文档)

    Python, 是一种面向对象.直译式计算机程序设计语言.Python语法简捷而清晰,具有丰富和强大的类库.它常被昵称为胶水语言,它能够很轻松的把用其他语言制作的各种模块(尤其是C/C++)轻松地联结 ...

  6. python/ORM操作详解

    一.python/ORM操作详解 ===================增==================== models.UserInfo.objects.create(title='alex ...

  7. 【python进阶】详解元类及其应用2

    前言 在上一篇文章[python进阶]详解元类及其应用1中,我们提到了关于元类的一些前置知识,介绍了类对象,动态创建类,使用type创建类,这一节我们将继续接着上文来讲~~~ 5.使⽤type创建带有 ...

  8. Python开发技术详解PDF

    Python开发技术详解(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1F5J9mFfHKgwhkC5KuPd0Pw 提取码:xxy3 复制这段内容后打开百度网盘手 ...

  9. python之数据类型详解

    python之数据类型详解 二.列表list  (可以存储多个值)(列表内数字不需要加引号) sort s1=[','!'] # s1.sort() # print(s1) -->['!', ' ...

随机推荐

  1. 【C#】#102 发送邮件

    项目需求:定时的发送邮件,于是学习了如何发送邮件 下面有一个简单的例子.能够实现简单的发送邮件,加上附件可以添加一个属性[Attachment],然后配置上附件的路径 Demo下载 代码总共只有一下这 ...

  2. 【C#】#100 调用摄像头

    需求:由于项目需要获得用户的头像,所以需要用C#调用摄像头获取头像. 下面写一个调用摄像头的方法 案例:调用摄像头的一个DEMO[效果图] 使用的类库:AForge.dll   [Demo下载,Dem ...

  3. RabbitMQ学习以及与Spring的集成(三)

    本文介绍RabbitMQ与Spring的简单集成以及消息的发送和接收. 在RabbitMQ的Spring配置文件中,首先需要增加命名空间. xmlns:rabbit="http://www. ...

  4. 4719: [Noip2016]天天爱跑步

    Time Limit: 40 Sec Memory Limit: 512 MB Submit: 1986 Solved: 752 [Submit][Status][Discuss] Descripti ...

  5. haproxy 启动错误

    在haproxy启动时会报错 ALERT] / () : Starting proxy short_message: cannot bind socket 问题1,如果bind的是vip,则需要内核添 ...

  6. virtualbox+vagrant学习-3-Vagrant Share-6-Custom Provider

    ⚠️警告:只是一个高级主题! 这个话题与开发vagrant插件有关.如果你对此不感兴趣,或者你刚刚开始使用vagrant,跳过这一页是安全的. 如果你正在开发一个 custom Vagrant pro ...

  7. Srv数据格式

    1.简介 类似msg文件, srv文件是用来描述服务( service数据类型的, service通信的数据格式定义在*.srv中. 它声明了一个服务, 包括请求(request)和响应( reply ...

  8. 测试oracle表空间自动扩展

    2019-04-1116:01:25 表空间分配10m自动扩展,向表中插入数据,看表空间达到10m以后是否会报错. 测试过程如下: 1.创建表空间 CREATE TABLESPACE TEST DAT ...

  9. ubuntu 服务器配置

    一.apache.svn服务器的搭建 1.安装apache2 apt-get install apache2 2.下载安装svn服务和svn-apache连接库 sudo apt-get instal ...

  10. mysql/mariadb学习记录——查询

    连接查询:同时设计两个及以上的表的查询 连接条件或连接谓词:用来连接两个表的条件一般格式: [<表名1>]<列名1> <比较运算符> [<表名2>]&l ...