首页 > 编程语言 >[Python自动化]使用Python Pexpect模块实现自动化交互脚本使用心得

[Python自动化]使用Python Pexpect模块实现自动化交互脚本使用心得

时间:2023-04-25 16:47:03浏览次数:51  
标签:pexpect Python 自动化 -- sh expect https child Pexpect

使用Python Pexpect模块实现自动化交互脚本使用心得

参考文档:https://pexpect.readthedocs.io/en/stable/

前言

在最近的工作中,需要使用DockerFile构建镜像。在构建镜像的过程中,有一些执行的命令是需要交互的。例如安装tzdata(apt install tzdata),不过在使用apt安装时,可以直接使用DEBIAN_FRONTEND=noninteractive 前缀来取消交互(至于是禁止交互还是选择交互的默认值,这一点就不太清楚了,TODO)。具体的命令行就是DEBIAN_FRONTEND=noninteractive apt install -y tzdata。在Dockerfile中也可以使用ARG进行统一设置。不过这种前缀设置方法仅仅适用于apt(大概TODO)。还有另一种我一开始就想到的方法,也就是利用类Unix自带的管道(pipe)功能,实现进程间通信,或是将stdin文件描述符重定向为某个文本或是字符串。按道理这是可行的,但是经过我的测试,不知道为啥行不通(等待探索TODO)。

Docker镜像中需要构建一个rust环境,因此需要安装rust。安装rust的方法一般有两种

  • 使用官方推荐的 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 命令进行操作,这个命令首先下载脚本,然后将输出的脚本通过管道作为sh进程的输入(pipe,fork,exec,dup)。sh执行脚本的过程中会遇到一些交互,如果这时候将sh的stdin重定向到预定好的文件或是字符串,按道理是可以直接进行自动化交互的,至于为啥没能成功。。咱也不知道 (curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh) < in.in 为啥不能成功捏。
  • 直接使用apt install rust-all。这种方法确实方便,也没有任何交互,但是很多配置因为由于和rust官方可能不一样,很多环境变量没有设置($CARGO_HOME),有些时候还需要自己配置,属实是麻烦得很。

因此我迫切需要一个可以自动化交互的方法。在网上找了很久答案后,发现pexpect可以实现这种自动化交互。因此在这里需要学习pexpect的相关用法。(shell脚本中也有expect相关概念,但是由于shell脚本我用起来感觉有点不太适应,因此就用python了)。

Pexpect简介

Pexpect allows your script to spawn a child application and control it as if a human were typing commands.

Pexpect can be used for automating interactive applications such as ssh, ftp, passwd, telnet, etc. It can be used to a automate setup scripts for duplicating software package installations on different servers. It can be used for automated software testing.

一个中心:自动化,各种需要交互,需要输入都可以自动化。

安装

使用pip包管理进行安装

pip install pexpect -i https://pypi.tuna.tsinghua.edu.cn/simple/

在这里用清华源进行加速。

This version of Pexpect requires Python 3.3 or above, or Python 2.7.

以下的流程均在Ubuntu上进行,Windows等系统使用pexpect请参考:https://pexpect.readthedocs.io/en/stable/overview.html#windows

基本操作

在py脚本中,定义想要匹配的提示,然后进行对应的输出。其中匹配可以是字符串完全匹配也可以是正则表达式状态机匹配。

  • 通过pexpect.spawn方法进行脚本的执行
  • 配置expect,从而捕获匹配的字符串
  • 配置对应expect的响应

example

# This connects to the openbsd ftp site and
# downloads the recursive directory listing.
import pexpect
child = pexpect.spawn('ftp ftp.openbsd.org')
child.expect('Name .*: ')
child.sendline('anonymous')
child.expect('Password:')
child.sendline('[email protected]')
child.expect('ftp> ')
child.sendline('lcd /tmp')
child.expect('ftp> ')
child.sendline('cd pub/OpenBSD')
child.expect('ftp> ')
child.sendline('get README')
child.expect('ftp> ')
child.sendline('bye')

注意事项

  1. 惊天巨坑

    由于匹配的字符串可以是正则表达式也可以是普通字符串,因此有些符号是需要转义的。
    比如我下面这个脚本

    import pexpect
    import sys
    child = pexpect.spawn('bash -c \'curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh\'')
    child.expect("Continue\? \(y\/N\)")
    print("get 1")
    child.sendline("y")
    child.expect('>')
    print("get 2")
    child.sendline('1')
    child.expect(pexpect.EOF)
    

    如果child.expect("Continue\? \(y\/N\)")这句代码没有使用转义符号,那么就将卡死,这玩意卡了我半小时,属实是折磨了。

    惊天巨坑

  2. 当命令行中使用(>>,<<,|)等符号的时候,直接spwan+命令行是不起作用的,需要额外调用shell进行操作

    child = pexpect.spawn('bash -c \'curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh\'')
    

    对应:curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh

  3. If you wish to read up to the end of the child’s output without generating an EOF exception then use the expect(pexpect.EOF) method.

    正如文档中所说,不管怎么样,尽量都整一个expect(pexpect.EOF)来把EOF给吞掉

探索

有个问题,expect是顺序执行的还是随机匹配的呢?
可以使用一个没有安装rust的机器进行测试。没有安装rust的机器不会出现Continue\? \(y\/N\)这一提示。查看expect是否会跳过。实测不会跳过,会卡住。

经过文档的查看,使用

index = p.expect(['good', 'bad', pexpect.EOF, pexpect.TIMEOUT])
if index == 0:
    do_something()
elif index == 1:
    do_something_else()
elif index == 2:
    do_some_other_thing()
elif index == 3:
    do_something_completely_different()

index的操作,输入多个可匹配字符串,进行匹配,遇到哪个就执行哪个。

所以对于无序乱序输出的程序,可以使用一个循环,然后任意匹配。可以自定义一个状态机,通过输入的顺序决定执行的流程。

try:
    index = p.expect(['good', 'bad'])
    if index == 0:
        do_something()
    elif index == 1:
        do_something_else()
except EOF:
    do_some_other_thing()
except TIMEOUT:
    do_something_completely_different()

在上面这段代码中,EOF就可能是终止这个状态机,TIMEOUT也有可能会中止状态机。

最终Rust自动化无交互安装脚本如下

import pexpect
child = pexpect.spawn('bash -c \'curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh\'')
try:
    while True:
        index = child.expect(['Continue\? \(y\/N\)','>'])
        if index == 0:
            child.sendline('y')
            print("Continue? (y/N) y")
        elif index == 1:
            child.sendline('1')
            print("> 1")
except pexpect.EOF:
    exit
except pexpect.TIMEOUT:
    print("timeout")

碎碎念

在我寻找答案的过程中,还发现了这么一个答案

curl https://sh.rustup.rs -sSf | sh -s -- -y

使用这个方法可以直接无交互自动化安装,但是这是一个特解。应用程序千千万万,如果每个应用程序都有特殊的无交互方式,那么需要了解所有的相关操作,这不免有点太累了。好处呢,就是这个方案基本上是永久有效,能够跟着应用程序更新。坏处呢也就是这只是一个特解,pexpect这个通解还是需要的。

标签:pexpect,Python,自动化,--,sh,expect,https,child,Pexpect
From: https://www.cnblogs.com/alyjay/p/17353067.html

相关文章

  • go语言 把python项目打包、变量类型、常量、函数基础、函数高级
    把python项目打包#https://zhuanlan.zhihu.com/p/624648232#python----》setuptools--》whl包结构公司内部写了包---》公司内部用---》开源出来公司写好的项目,打包好,发给客户,客户可以直接运行起来#你们下的第三方包都是:requests-2.28.2-py3-none-any.whlwhl......
  • 仿Django框架-基于wsgiref模块和jinja2模块写一个简单的框架 主流框架简介 动静态网
    目录仿Django框架-基于wsgiref模块和jinja2模块写一个简单的框架一、前期需要的了解背景知识web框架的本质理解1:连接前端与数据库的中间介质理解2:socket服务端手写web框架的大概思路1.编写socket服务端代码2.浏览器访问响应无效>>>:HTTP协议3.根据网址后缀的不同获......
  • [oeasy]python0140_导入_import_from_as_namespace_
    导入import回忆上次内容上次学习了tryexcept 注意要点半角冒号缩进输出错误信息 有错就报告不要隐瞒否则找不到出错位置还可以用traceback把系统报错信息原样输出  但是代码量好多啊10多行了......
  • 用Python实现十大经典排序算法
    用Python实现十大经典排序算法1.冒泡排序冒泡排序(BubbleSort)是一种比较简单的排序算法,它重复地走访过要排序的元素,依次比较相邻两个元素,如果它们的顺序错误就把他们调换过来,直到没有元素再需要交换,排序完成。算法过程比较相邻的元素,如果前一个比后一个大,就把它们两个对调位......
  • 【Python】操作复杂嵌套的json数据
    1、相关文章递归获取所有key-value值:https://www.cnblogs.com/phoenixy/p/17126455.html 2、对复杂的json进行增删改查①获取数据#-*-coding:UTF-8-*-importjsonfromjsonpath_ngimportparsefromaa_demo.base.loggerimport*classjson_labor_tools:......
  • 毕业5年的同学突然告诉我,他已经是年薪30W的自动化测试工程师....
    作为一名程序员,都会对自己未来的职业发展而焦虑。一方面是因为IT作为知识密集型的行业,知识体系复杂且知识更新速度非常快,“一日不学就会落后”。​另外一方面,IT又是劳动密集型的行业,不仅业人员多,而且个人在平时的开发过程中有大量的重复劳动(如CRUD),自己的能力没有随年龄的增加而......
  • Python语言学习讲解十六:python之描述符__set__和__get__ 等解释
    一、方法:首先说下python中存在的几种方法:对象方法、静态方法、类方法等,归属权分别为obj、cls、cls其实可以从他们的参数中就可以看的出来对象方法参数中含有self,这个类似于C++中的this指针。静态方法使用@staticmethod来修饰,可以通过类或类的实例对象来调用而已.1.>>>class2.......
  • Python语言学习讲解十九: 异常信息的详细获取
    由于近期忙着手游发布,所以这几天没有及时更新望各位学者见谅。年底了,各大公司特别是游戏行业都着手赶年底末班车,给用户一个新年的礼物。在项目中出现了一些异常日志,但是并没有记录到详细的错误信息。特别是报错在哪一个文件哪一行等信息。[python] viewplain ......
  • python读取文件创建时间
    #获取文件时间(浮点数格式)csv_time=os.path.getmtime("C:/Users/DELL/Desktop/20000/allqueryCommodity.csv")print("csv_time",csv_time)#结果:1682402963.033327#把浮点数格式格式转成格式化格式local_time=time.localtime(csv_time)print("local_time",local_tim......
  • Python中的时间格式的读取与转换(time模块)
    一、时间的表示格式在Python中,表示时间的格式有4种较为常用,分别是浮点数格式、标准可读格式、格式化格式以及自定义格式。(名字是自己起的,非官方命名)(1)浮点数格式用一个float格式的浮点数表示时间,其具体含义表示为从世界标准纪元时间(1970年1月1日)起算至该时间节点的秒数。(2)标准......