博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
系统批量运维管理器pexpect详解
阅读量:5153 次
发布时间:2019-06-13

本文共 8528 字,大约阅读时间需要 28 分钟。

一、pexpect介绍

pexpect可以理解成Linux下的expect的Python封装,通过pexpect我们可以实现对ssh、ftp、passwd、telnet等命令进行自动交互,而无需人工干涉来达到自动化的目的。比如我们可以模拟一个FTP登陆时的所有交互,包括输入主机地址、用户名、密码、上传文件等,待出现异常我们还可以进行尝试自动处理。

pexpect官网地址:

        

二、pexpect的安装

mac os安装

pip3 install pexpect

CentOS 安装

[root@localhost ~]# pip3 install pexpectCollecting pexpect  Downloading https://files.pythonhosted.org/packages/89/e6/b5a1de8b0cc4e07ca1b305a4fcc3f9806025c1b651ea302646341222f88b/pexpect-4.6.0-py2.py3-none-any.whl (57kB)    100% |████████████████████████████████| 61kB 128kB/sCollecting ptyprocess>=0.5 (from pexpect)  Downloading https://files.pythonhosted.org/packages/ff/4e/fa4a73ccfefe2b37d7b6898329e7dbcd1ac846ba3a3b26b294a78a3eb997/ptyprocess-0.5.2-py2.py3-none-any.whlInstalling collected packages: ptyprocess, pexpectSuccessfully installed pexpect-4.6.0 ptyprocess-0.5.2

三、pexpect的核心组件

spawn类

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

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

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

child = pexpect.spawn('/usr/bin/ftp')      #启动FTP客户端命令child = pexpect.spawn('/usr/bin/ssh user@example.com')    #启动ssh远程连接命令child = pexpect.spawn('ls -latr /tmp')    #运行ls显示/tmp目录内容命令

当子程序需要参数时,还可以使用Python列表代替参数项,如:

child = pexpect.spawn('/usr/bin/ftp', [])child = pexpect.spawn('/usr/bin/ssh', ['user@example.com'])child = pexpect.spawn('ls', ['-latr', '/tmp'])

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

需要注意的是,pexpect不会解析shell命令当中的元字符,包括重定向">"、管道"|"或通配符“*”,当然,我们可以通过一个技巧来解决这个问题,将存在着三个特殊元字符的命令作为/bin/bash的参数进行调用,例如:

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

我们可以通过将命令的参数以Python列表的形式进行替换,从而使我们的语法变成更加清晰,下面的代码等价于上面的。

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

有时候调式代码时,希望获取pexpect的输入与输出信息,以便了解匹配的情况。pexpect提供了两种途径,一种为写到日志文件,另一种为输出到标准输出。写到日志文件的方法如下:

child = pexpect.spawn('some_command')fout = open('mylog.txt','wb')child.logfile = fout

输出到标准输出的方法如下:

# In Python 2:child = pexpect.spawn('some_command')child.logfile = sys.stdout# In Python 3, we'll use the ``encoding`` argument to decode data# from the subprocess and handle it as unicode:child = pexpect.spawn('some_command', encoding='utf-8')child.logfile = sys.stdout

logfile_read和logfile_send成员可用于分别记录来自子项的输入和发送给子项的输出。有时你不想看到你给孩子写的所有内容。你只想记录孩子送回的东西。例如:

child = pexpect.spawn('some_command')child.logfile_read = sys.stdout

如果您使用的是Python 3,则需要在上面的代码中传递编码。

要单独记录发送给子级的输出使用logfile_send:

child.logfile_send = fout

注意:如果你想获得孩子的退出状态,你必须调用close()方法。孩子的出口或信号状态将存储在self.exitstatus或self.signalstatus中。如果孩子正常退出,exitstatus将存储退出返回码,signalstatus将为None。如果孩子被信号异常终止,那么signalstatus将存储信号值,exitstatus将为None:

child = pexpect.spawn('some_command')child.close()print(child.exitstatus, child.signalstatus)

更多细节,可以读取存储由os.waitpid返回的状态的self.status成员。你可以使用os.WIFEXITED/os.WEXITSTATUS 或 os.WIFSIGNALED/os.TERMSIG来解释它。

 

下面为一个完整的示例,实现远程SSH登录,登录成功后显示/usr/local/src/目录文件清单,并通过日志文件记录所有的输入与输出。

import pexpectimport syschild = pexpect.spawn('ssh root@192.168.56.132')fout = open('mylog.txt',mode='wb')child.logfile = fout#child.logfile = sys.stdoutchild.expect("(yes/no)?")child.sendline("yes")child.expect("password:")child.sendline("1234567")child.expect('#')child.sendline('/bin/ls /usr/local/src/')child.expect("#")

以下为mylog.txt日志内容,可以看到pexpect产生的全部输入与输出信息。

[root@localhost ~]# cat mylog.txtyesyesroot@192.168.56.132's password: 1234567Last login: Sat Jun  2 15:13:51 2018 from 192.168.56.131[root@localhost ~]# /bin/ls /usr/local/src//bin/ls /usr/local/src/Python-3.6.5  Python-3.6.5.tgz

expect方法

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

方法定义:expect(patterntimeout=-1searchwindowsize=-1async_=False**kw)

其中,参数pattern表示字符串、pexpect.EOF(指向缓冲区尾部,无匹配项)、pexpect.TIMEOUT(匹配等待超时)、正则表达式或者前面四种类型组成的列表(List),当pattern为一个列表时,且不止一个列表元素被匹配,则返回的结果是子程序输出最先出现的那个元素,或者是列表最左边的元素(最小索引ID),如:

import pexpectchild = pexpect.spawn("echo 'foobar'")print(child.expect(['bar','foo','foobar']))#输出:1即'foo'被匹配

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

当pexpect.EOF、pexpect.TIMEOUT作为expect的列表参数时,匹配时将返回所处列表中索引ID,例如:

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()

以上代码等价于

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()

expect方法有两个非常棒的成员:befoe与。before成员保存了最近匹配成功之前的内容,affer成员保存了最近匹配成功之后的内容。例如:

import pexpectchild = pexpect.spawn('ssh root@192.168.56.132',encoding='utf-8')fout = open('mylog.txt',mode='w')child.logfile = foutchild.expect("(yes/no)?")child.sendline("yes")child.expect(['password:'])child.sendline("1234567")print("before:"+child.before)print("after:"+child.after)

运行结果如下:

[root@localhost ~]# python3 simple2.pybefore:yesroot@192.168.56.132'safter:password:

read相关方法

下面这些输入方法的作用都是向子程序发送响应命令,可以理解成代替了我们的标准输入键盘。

send(self,s)   发送命令,不回车sendline(self, s='')   发送命令,回车sendcontrol(self, char)   发送控制字符,如child.sendcontrol('c')等价于"ctrl+c"sendof()       发送eof

四、run函数

run是使用pexpect进行封装的调用外部命令的函数,类似于os.system或os.popen方法,不同的是使用run()可以同时获得命令的输出结果及命令的退出状态,函数定义: pexpect.run(command, timeout=30, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None, **kwargs)。

参数command可以是系统已知的任意命令,如没有写绝对路径时将会尝试搜索命令的路径,events是一个字典,定义了expect及sendline方法的对应关系,spawn方式的例子如下:

from pexpect import *child = spawn('scp foo user@example.com:.')child.expect('(?i)password')child.sendline(mypassword)

使用run函数实现如下,是不是更加简洁、精炼了?

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

pxssh类

该类将pexpect.spawn扩展为专门设置SSH连接。增加了登录,注销和期望的shell提示的方法。它执行各种棘手的事情来处理SSH登录过程中的许多情况。例如,如果会话是您第一次登录,则pxssh会自动接收远程证书;或者你有公钥认证设置,则pxssh不会等待密码提示。pxssh使用shell提示符来同步来自远程主机的输出。为了使它更健壮,它将shell提示设置为比$或#更独特的东西。这应该适用于大多数Borne/Bash or Csh 风格的 shell。

pxssh类定义:

classpexpect.pxssh.pxssh(timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None, ignore_sighup=True, echo=True, options={}, encoding=None, codec_errors='strict', debug_command_string=False)

pxssh常用的三个方法如下:

  • login()建立ssh连接;
  • logout()断开连接;
  • prompt()等待系统提示符,用于等待命令执行结束。

下面使用pxssh类实现一个ssh连接远程主机并执行命令的示例。首先使用login()方法与远程主机建立连接,再通过sendline()方法发送执行的命令,prompt()方法等待命令执行结束且出现系统提示符,最后使用logout()方法断开连接。

from pexpect import pxsshimport getpasstry:    s = pxssh.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()               #断开ssh连接except pxssh.ExceptionPxssh as e:    print("pxssh failed on login.")    print(e)

五、pexpect应用实例

远程文件自动打包并下载

import pexpectimport sysip='192.168.56.132'         #定义目标主机user='root'                 #目标主机用户passwd='1234567'          #目标主机密码target_file='/data/logs/nginx_access.log'           #目标主机nginx日志文件child = pexpect.spawn('/usr/bin/ssh', [user+'@'+ip],encoding='utf-8')       #运行ssh命令fout = open('mylog.txt','w')                                #输入、输出日志写入mylog.txt文件child.logfile = fouttry:    child.expect('(?i)password')            #匹配password字符串,(?i)表示不区别大小写    child.sendline(passwd)    child.expect('#')    child.sendline('tar -zcPf /data/nginx_access.tar.gz ' +target_file)       #打包nginx日志文件    child.expect('#')    print(child.before)    child.sendline('exit')    fout.close()except EOFError:       #定义EOF异常处理    print('expect EOF')except TimeoutError:        #定义timeout异常处理    print('expect timeout')child = pexpect.spawn('/usr/bin/scp', [user+'@'+ip+':/data/nginx_access.tar.gz','/home'],encoding='utf-8')  #启动scp远程拷贝命令,实现将打包好的nginx日志复制到本地/home目录下fout = open('mylog.txt','a')child.logfile = fouttry:    child.expect('(?i)password')    child.sendline(passwd)    child.expect(pexpect.EOF)      #匹配缓冲区EOF(结尾),保证文件复制正常完成except EOFError:    print('expect EOF')except TimeoutError:    print('expect timeout')

 

转载于:https://www.cnblogs.com/hwlong/p/9125277.html

你可能感兴趣的文章
NWERC 2012 Problem E Edge Case
查看>>
JQuery选择器总结
查看>>
sql 触发器删除操作
查看>>
第一次作业
查看>>
《JavaScript高级程序设计》学习笔记——最佳实践
查看>>
【CCPC-Wannafly Winter Camp Day3 (Div1) G】排列(水题)
查看>>
js 中三元运算符的运用
查看>>
DLL函数重定向
查看>>
docker 常用命令
查看>>
Python学习笔记--8.7 函数--可变参数、关键字参数
查看>>
四则运算的代码的改进(三)
查看>>
thinkphp 内置标签volist 控制换行
查看>>
使用存储过程和视图存储配置
查看>>
数据分析告诉我们的四个经验教训
查看>>
ReportView动态加载带参数的RDCL文件及子报表
查看>>
inside tomcat 6, 环境搭建
查看>>
INF6027 Introduction to Data Science Analysis of the UK Police Dataset
查看>>
设计模式(创建型模式-单例模式)
查看>>
超市收银系统_定义商品的父类和各个子类的实现——1
查看>>
LAMP环境搭建(Ubuntu)
查看>>