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 获取et和excel的版本号
Apr 09 Python
python 编程之twisted详解及简单实例
Jan 28 Python
TensorFlow模型保存和提取的方法
Mar 08 Python
Python使用pip安装pySerial串口通讯模块
Apr 20 Python
基于DataFrame改变列类型的方法
Jul 25 Python
解决使用pycharm提交代码时冲突之后文件丢失找回的方法
Aug 05 Python
浅谈Python访问MySQL的正确姿势
Jan 07 Python
Pycharm插件(Grep Console)自定义规则输出颜色日志的方法
May 27 Python
利用Python批量识别电子账单数据的方法
Feb 08 Python
python爬虫今日热榜数据到txt文件的源码
Feb 23 Python
基于Python的EasyGUI学习实践
May 07 Python
PyTorch中permute的使用方法
Apr 26 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
php截取中文字符串不乱码的方法
2013/12/25 PHP
PHP原生函数一定好吗?
2014/12/08 PHP
php生成mysql的数据字典
2016/07/07 PHP
在laravel中实现将查询的对象转换为多维数组的函数
2019/10/21 PHP
PHP call_user_func和call_user_func_array函数的简单理解与应用分析
2019/11/25 PHP
php计数排序算法的实现代码(附四个实例代码)
2020/03/31 PHP
JavaScript高级程序设计
2006/12/29 Javascript
javascript面向对象之对象的深入理解
2015/01/13 Javascript
JS两种类型的表单提交方法实例分析
2016/11/28 Javascript
bootstrap table分页模板和获取表中的ID方法
2017/01/10 Javascript
vue如何引用其他组件(css和js)
2017/04/13 Javascript
React Native使用百度Echarts显示图表的示例代码
2017/11/07 Javascript
es6在react中的应用代码解析
2017/11/08 Javascript
vue 子组件向父组件传值方法
2018/02/26 Javascript
详解Vue组件插槽的使用以及调用组件内的方法
2018/11/13 Javascript
javascript数据类型中的一些小知识点(推荐)
2019/04/18 Javascript
python使用scrapy解析js示例
2014/01/23 Python
使用python编写脚本获取手机当前应用apk的信息
2014/07/21 Python
Python创建日历实例
2014/08/21 Python
Python学习之asyncore模块用法实例教程
2014/09/29 Python
使用C语言扩展Python程序的简单入门指引
2015/04/14 Python
python 捕获 shell/bash 脚本的输出结果实例
2017/01/04 Python
python控制nao机器人身体动作实例详解
2019/04/29 Python
使用PyCharm进行远程开发和调试的实现
2019/11/04 Python
numpy:np.newaxis 实现将行向量转换成列向量
2019/11/30 Python
使用keras根据层名称来初始化网络
2020/05/21 Python
python查看矩阵的行列号以及维数方式
2020/05/22 Python
python如何爬取动态网站
2020/09/09 Python
利用python如何实现猫捉老鼠小游戏
2020/12/04 Python
W3C公布最新的HTML5标准草案
2008/10/17 HTML / CSS
经典优秀个人求职信分享
2013/12/12 职场文书
工厂总经理岗位职责
2014/02/07 职场文书
物理系毕业生自荐书范文
2014/02/22 职场文书
房产委托公证书样本
2014/04/04 职场文书
庭外和解协议书
2016/03/23 职场文书
创业计划书之家政服务
2019/09/18 职场文书