Python pexpect模块及shell脚本except原理解析


Posted in Python onAugust 03, 2020

expect脚本

expect是什么

expect是一个免费的编程工具,用来实现自动的交互式任务,而无需人为干预。说白了,expect就是一套用来实现自动交互功能的软件。

在实际工作中,我们运行命令、脚本或程序时,这些命令、脚本或程序都需要从终端输入某些继续运行的指令,而这些输入都需要人为的手工进行。而利用expect,则可以根据程序的提示,模拟标准输入提供给程序,从而实现自动化交互执行

由于在linux中的一些命令不太适合于脚本的自动化运行,比如fdisk,telnet,ftp连接下载等,所以必须使用except来解决交换问题。

except基础

包含以下四个命令

命令 作用
send 用于向进程发送字符串
except 从进程接收字符串
spwan 启动新进程
interact 允许用户交互
  • send命令接收一个字符串参数,并将该参数发送到进程。
  • expect命令和send命令相反,expect通常用来等待一个进程的反馈,我们根据进程的反馈,再发送对应的交互命令。
  • spawn命令用来启动新的进程,spawn后的send和expect命令都是和使用spawn打开的进程进行交互。
  • interact命令用的其实不是很多,一般情况下使用spawn、send和expect命令就可以很好的完成我们的任务;但在一些特殊场合下还是需要使用interact命令的,interact命令主要用于退出自动化,进入人工交互。比如我们使用spawn、send和expect命令完成了ftp登陆主机,执行下载文件任务,但是我们希望在文件下载结束以后,仍然可以停留在ftp命令行状态,以便手动的执行后续命令,此时使用interact命令就可以很好的完成这个任务。

代码举例

#!/usr/bin/expect

set timeout 30
set host "101.200.241.109"
set username "root"
set password "123456"

spawn ssh $username@$host
expect "*password*" {send "$password\r"}
interact

这是一段非常简单代码,演示了基本用法

#!/usr/bin/expect:使用expect来解释该脚本;

set timeout 30:设置超时时间,单位为秒,默认情况下是10秒;

set host "101.200.241.109":设置变量;

spawn ssh $username@$host:spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。它主要的功能是给ssh运行进程加个壳,用来传递交互指令;

expect "password":这里的expect也是expect的一个内部命令,这个命令的意思是判断上次输出结果里是否包含“password”的字符串,如果有则立即返回;否则就等待一段时间后返回,这里等待时长就是前面设置的30秒;

send "$password\r":当匹配到对应的输出结果时,就发送密码到打开的ssh进程,执行交互动作;

interact:执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。

这就是对上述这段简单简单脚本的分析,在上述的示例中,涉及到expect中一个非常重要的概念——模式-动作;即上述expect "password" {send "$password\r"}这句代码表达出来的含义。

模式-动作

结合着expect "password" {send "$password\r"}这句代码来说说“模式-动作”。简单的说就是匹配到一个模式,就执行对应的动作;匹配到password字符串,就输入密码

如下所示:

expect {
  "password" {
    send "$password\r"
    exp_continue
  }
  eof
  {
    send "eof"
  }
}

其中exp_continue表示循环式匹配,通常匹配之后都会退出语句,但如果有exp_continue则可以不断循环匹配,输入多条命令,简化写法。

传参

很多时候,我们需要传递参数到脚本中,现在通过下面这段代码来看看如何在expect中使用参数:

#!/usr/bin/expect

if {$argc < 3} {
  puts "Usage:cmd <host> <username> <password>"
  exit 1
}

set timeout -1
set host [lindex $argv 0] 
set username [lindex $argv 1]
set password [lindex $argv 2]

spawn ssh $username@$host
expect "*password*" {send "$password\r"}
interact

在expect中,\$argc表示参数个数,而参数值存放在$argv中,比如取第一个参数就是[lindex $argv 0],以此类推。

FTP下载expect脚本

使用yum安装expect

yum install expect

按照如下编写expect脚本

#!/usr/bin/expect -f 
set ip [lindex $argv 0]                           #脚本的第一个参数,远程主机的IP地址
set file [lindex $argv 1]                          #脚本的第二个参数,指定下载的文件名
set timeout 10                                #设置超时时间10秒
spawn ftp $ip                                 #运行ftp $ip命令
expect "Name*"                               #如果出现Name字符
send "anonymous \r"                           #则输入anoymous(匿名用户)并回车
expect "Password:*"                           #如果出现Password字符
send "\r"                                     #则仅输入回车
expect "ftp>*"                                 #如果出现ftp>字符
send "get $file\r"                               #则发送get $file命令
expect {
  "*Failed*" { send_user " Download failed\r";send "quit\r"}  #如果返回的字符串有Failed,则说明下载失败,send_user回显信息 Download failed 
  "*send*" { send_user " Download ok\r";send "quit\r"} #如果返回的字符串有send,则说明下载失败,send_user回显信息 Download ok
  }
expect eof  #结束循环匹配

给脚本加上可执行权限chmod +x expect_ftp_auto.exp

pexpect模块

pexpect可以理解为linux下的expect的python封装,通过pexpect可以实现ssh,ftp,passwd,telnet等命令的进行自动交互

安装pip install pexpect

简单实现ssh自动登录的示例如下:

import pexpect
child = pexpect.spwan('scp foo user#expample.com:.')  #spwan启动scp程序
child.expect('Password:')          #expect方法等待子程序产生的输出,判断是否匹配定义的字符串
                                            #‘Password:'
child.sendline(mypassword)       #匹配后则发送密码进行回应

核心组件

spawn类

spawn是pexpect的主要入口,功能是启动和控制子应用程序,以下是它的构造函数

class pexpect.spwan(command,args=[],timeout=30,maxread=2000,searchwindowsize=None,logfile=None,cwd=None,env=None,ignore_sighup=True)

其中,command参数可以是任意已知的系统命令,比如

child=pexpect.spawn('user/bin/ftp')

当子程序需要参数的时候,还可以使用python列表来代替参数,如

child = pexpect.spwan('user/bin/ssh user@example.com')

参数timeout为等待结果的超时时间,maxread为从终端控制台一次读取的最大字节数,searchwindowsize参数为匹配的缓冲区字符串的位置,默认是从开始位置匹配

需要注意的是,pexpext不会解析shell命令中的元字符,包括重定向> 管道|或者通配符,此时可以将三个特殊元字符的命令作为/bin/bash的参数进行调用

child =expect.spwan('/bin/bash -c "ls -l | grep LOG> logs.txt"')
child.expect(pexpect.EOF)

可以通过将命令的参数以PYTHON列表的方式进行替换,从而使得语法更加清晰,下面的代码等同于上面的代码

shell_cmd='ls -l | grep LOG >logs.txt'
child=pexpext.spwan('/bin/bash',['-c,shell_cmd])
child.expect(pexpect.EOF)

在调试代码时,希望获取pexpect的输入与输出信息,以便了解匹配的情况,一种时写到日志中,另一种时输出到标准输出

写到日志中

chidl-pexpect.spwan('some_command')
fout=file('mylog.txt,'w')
child.logfile=fout

输出到标准输出的方法

child=pexpect.swpan('some_command')
chuld.logfile=sys.stdout

以下为SSH远程登录举例,登录成功后显示/home目录的文件并且记录输入与输出

import pexpect
import sys

child=pexpect.spawn('ssh root@172.31.208.129')
fout=open('mylog.txt','w')
child.logfile=fout
#child.logfile=sys.stdout

child.expect('password:')
child.sendline('abc@123')
child.expect('#')
child.sendline('ls /home')
child.expect('#')

expect方法

expect定义了子程序输出的匹配规则

方法定义:expect(pattern,timeout=-1,searchwindowsize=-1)

其中,参数pattern表示字符串,pexpext.EOF(指向缓冲区,无匹配项)、pexpect,TIMEOUT(匹配等待超时),正则表达式或者列表

参数timeout指定了等待匹配结果的超时时间,单位为秒,当超时被触发的时候,expect将匹配到pexpext.TIMEOUT,参数searchwindowsize为匹配的缓冲字符串的位置,默认时从开始的位置匹配

read相关方法

下面的的方法作用都是向子程序发送响应命令

send(self,s) #发送命令,不回车
sendline(self,s=' '),#发送命令,回车
snedcontrol(self.char) #发送控制字符
sendeof() #发送eof

run函数

run时使用pexpext进行封装的调用外部命令的的函数

from pexpect import *
run('scp foo user@example.com:.',events={'(?i)password':mypassword])

pxssh类

针对ssh会话操作上再做一次封装

class pexpext.pxssh.pxssh(timeout=30,maxread=2000,searchwindwosize=None,logfile=None,cwd=None,env=None)

常用方法

  • login()建立ssh连接
  • logout() 断开连接
  • promp()等待系统提示符,用于等待命令执行结束
import pxssh
import getpass
try:
  s = pxssh.pxssh()                 #创建对象s
  hostname = raw_input('hostname: ')
  username = raw_input('username: ')
  password = getpass.getpass('password: ')  #接收密码输入
  s.login (hostname, username, password)   #建立ssh连接
  s.sendline ('uptime') # 运行uptime命令
  s.prompt()       # 匹配系统提示符
  print s.before     # 打印系统体术符号出现前的命令输出
  s.sendline ('ls -l')
  s.prompt()
  print s.before
  s.sendline ('df')
  s.prompt()
  print s.before
  s.logout()
except pxssh.ExceptionPxssh, e:
  print "pxssh failed on login."
  print str(e)

FTP自动操作

实现自动交互登录FTP操作

import pexpect
import sys

child = pexpect.spawnu('ftp ftp.openbsd.org') #运行ftp命令
child.expect('(?i)name .*: ')   #(?!)表示后面的字符串正则表达式忽略大小写
child.sendline('anonymous')  # 输入ftp账号信息
child.expect('(?i)password')  #匹配密码提示
child.sendline('pexpect@sourceforge.net')  
child.expect('ftp> ')
child.sendline('bin')   #启用二进制传输
child.expect('ftp> ')
child.sendline('get robots.txt') 
child.expect('ftp> ')
sys.stdout.write (child.before)  #输出匹配的"ftp"之前的输入与输出操作
print("Escape character is '^]'.\n")
sys.stdout.write (child.after)
sys.stdout.flush()
child.interact() # Escape character defaults to ^]  #让出控制权,用户可以继续当前会话手工控制子程序,默认输入"^]"字符跳出
child.sendline('bye')
child.close()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
理解Python中的With语句
Feb 02 Python
Python中内置的日志模块logging用法详解
Jul 12 Python
python技能之数据导出excel的实例代码
Aug 11 Python
python爬虫之模拟登陆csdn的实例代码
May 18 Python
教你利用Python玩转histogram直方图的五种方法
Jul 30 Python
如何用Python来理一理红楼梦里的那些关系
Aug 14 Python
python实现密度聚类(模板代码+sklearn代码)
Apr 27 Python
Python使用pdb调试代码的技巧
May 03 Python
解决Keyerror ''acc'' KeyError: ''val_acc''问题
Jun 18 Python
python else语句在循环中的运用详解
Jul 06 Python
python 实现非极大值抑制算法(Non-maximum suppression, NMS)
Oct 15 Python
Python数据可视化之Seaborn的安装及使用
Apr 19 Python
python爬虫使用正则爬取网站的实现
Aug 03 #Python
python获取整个网页源码的方法
Aug 03 #Python
flask开启多线程的具体方法
Aug 02 #Python
基于opencv实现简单画板功能
Aug 02 #Python
django下创建多个app并设置urls方法
Aug 02 #Python
Django如何在不停机的情况下创建索引
Aug 02 #Python
如何用Anaconda搭建虚拟环境并创建Django项目
Aug 02 #Python
You might like
在Windows下编译适用于PHP 5.2.12及5.2.13的eAccelerator.dll(附下载)
2010/05/04 PHP
PHP 异步执行方法,模拟多线程的应用分析
2013/06/03 PHP
php利用curl抓取新浪微博内容示例
2014/04/27 PHP
微信支付开发交易通知实例
2016/07/12 PHP
php微信公众号开发模式详解
2016/11/28 PHP
PHP如何获取当前主机、域名、网址、路径、端口等参数
2017/06/09 PHP
ajax+php实现无刷新验证手机号的实例
2017/12/22 PHP
php设计模式之抽象工厂模式分析【星际争霸游戏案例】
2020/01/23 PHP
php设计模式之原型模式分析【星际争霸游戏案例】
2020/03/23 PHP
深入认识javascript中的eval函数
2009/11/02 Javascript
用jQuery模拟页面加载进度条的实现代码
2011/12/19 Javascript
文本框获得焦点和失去焦点的判断代码
2012/03/18 Javascript
GridView中获取被点击行中的DropDownList和TextBox中的值
2013/07/18 Javascript
JavaScript字符串对象fromCharCode方法入门实例(用于把Unicode值转换为字符串)
2014/10/17 Javascript
纯Javascript实现ping功能的方法
2015/03/20 Javascript
jQuery实现的产品自动360度旋转展示特效源码分享
2015/08/21 Javascript
jquery拖拽排序简单实现方法(效果增强版)
2016/02/16 Javascript
老生常谈遮罩层 滚动条的问题
2016/04/29 Javascript
JQuery获取鼠标进入和离开容器的方向
2016/12/29 Javascript
Node.js对MongoDB数据库实现模糊查询的方法
2017/05/03 Javascript
面试题:react和vue的区别分析
2019/04/08 Javascript
javascript将扁平的数据转为树形结构的高效率算法
2020/02/27 Javascript
零基础写python爬虫之抓取百度贴吧并存储到本地txt文件改进版
2014/11/06 Python
Tensorflow之构建自己的图片数据集TFrecords的方法
2018/02/07 Python
python实现抖音点赞功能
2019/04/07 Python
python画图把时间作为横坐标的方法
2019/07/07 Python
Python爬虫:将headers请求头字符串转为字典的方法
2019/08/21 Python
pycharm实现print输出保存到txt文件
2020/06/01 Python
建筑行业的大学生自我评价
2013/12/08 职场文书
干部现实表现材料
2014/02/13 职场文书
后勤服务中心总经理工作职责
2014/03/03 职场文书
《春雨》教学反思
2014/04/24 职场文书
关于读书的演讲稿800字
2014/08/27 职场文书
房屋鉴定委托书范本
2014/09/23 职场文书
党员组织生活会发言材料
2014/10/17 职场文书
2019生态环境保护倡议书!
2019/07/03 职场文书