系统批量运维管理器pexpect详解

Posted hwlong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了系统批量运维管理器pexpect详解相关的知识,希望对你有一定的参考价值。

一、pexpect介绍

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

pexpect官网地址:https://pexpect.readthedocs.io/en/stable/

        https://pypi.org/project/pexpect/

二、pexpect的安装

mac os安装

pip3 install pexpect

CentOS 安装

[[email protected] ~]# pip3 install pexpect
Collecting pexpect
  Downloading https://files.pythonhosted.org/packages/89/e6/b5a1de8b0cc4e07ca1b305a4fcc3f9806025c1b651ea302646341222f88b/pexpect-4.6.0-py2.py3-none-any.whl (57kB)
    100% |████████████████████████████████| 61kB 128kB/s
Collecting ptyprocess>=0.5 (from pexpect)
  Downloading https://files.pythonhosted.org/packages/ff/4e/fa4a73ccfefe2b37d7b6898329e7dbcd1ac846ba3a3b26b294a78a3eb997/ptyprocess-0.5.2-py2.py3-none-any.whl
Installing collected packages: ptyprocess, pexpect
Successfully 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 [email protected])    #启动ssh远程连接命令
child = pexpect.spawn(ls -latr /tmp)    #运行ls显示/tmp目录内容命令

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

child = pexpect.spawn(/usr/bin/ftp, [])
child = pexpect.spawn(/usr/bin/ssh, [[email protected]])
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, well 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 pexpect
import sys

child = pexpect.spawn(ssh [email protected])
fout = open(mylog.txt,mode=wb)
child.logfile = fout
#child.logfile = sys.stdout

child.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产生的全部输入与输出信息。

[[email protected] ~]# cat mylog.txt
yes
yes
[email protected]192.168.56.132s password: 1234567

Last login: Sat Jun  2 15:13:51 2018 from 192.168.56.131
[[email protected] ~]# /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 pexpect
child = 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 pexpect


child = pexpect.spawn(ssh [email protected],encoding=utf-8)
fout = open(mylog.txt,mode=w)
child.logfile = fout

child.expect("(yes/no)?")
child.sendline("yes")

child.expect([password:])
child.sendline("1234567")
print("before:"+child.before)
print("after:"+child.after)

运行结果如下:

[[email protected] ~]# python3 simple2.py
before:yes
[email protected]192.168.56.132s
after:password:

read相关方法

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

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

 

以上是关于系统批量运维管理器pexpect详解的主要内容,如果未能解决你的问题,请参考以下文章

第十八章 Python批量管理主机(paramikofabric与pexpect)

系统批量运维管理器Fabric详解

pexpect之命令行程序进行自动交互

python运维之使用python进行批量管理主机

运维简介&Ansible详解

python实现主机批量管理