pexpect是python中用于实现SSH,FTP,telnet等命令进行自动化交互,从而无需人工干预实现自动化运维的一个第三方扩展模块。理论的描述过于抽象,这里首先设想一下,如果让您设计一个实现自动化模块的模型您会怎么设计,哦,我们只需要做简单的执行或开始操作,详细的过程程序自动执行,完成后返回对应的执行结果给我,那如何实现呢,对比一下自动化和非自动化的区别就会有思路,举一个简单SSH登录192.168.1.10服务器,然后执行一个ifconfig命令来查看网卡状态的工作为例,在非自动化场景下,首先需要调用SSH客户端执行ssh 192.168.1.10登录,接着登录窗口提示输入登录名,输入用户名后继续提示输入密码,登录成功后再执行ifconfig命令,得到返回。从这个过程可以看出,这个工作在完成过程中需要不断的交互,服务器返回消息,客户端根据服务器提示输入,那实现自动化最简单的方法就是预先知道服务器会依次有哪些返回,在得到返回后自动完成输入就可以屏蔽交互式,实现自动化操作。pexpect的设计正是遵循这个思路,接下来详细讲解。
一. pexpect的安装
作为python的一个普通模块,pexpect支持pip,easy_install或源码安装方式,具体命令及过程如下:
pip install pexpect
easy_install pexpect
wget https://github.com/pexpect/pexpect/releases/download/3.0/pexpect-3.0.tar.gz
tar -zxvf pexpect-3.0.tar.gz
cd pexpect-3.0
python setup.py install
完成后执行import pexpect和dir(pexpect)命令验证一下导入成功,如下:
Python 2.7.4 (default, Feb 24 2016, 11:19:23)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-16)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pexpect
>>> dir(pexpect)
['EOF', 'ExceptionPexpect', 'Expecter', 'PY3', 'TIMEOUT', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__revision__', '__version__', 'exceptions', 'expect', 'is_executable_file', 'pty_spawn', 'run', 'runu', 'searcher_re', 'searcher_string', 'spawn', 'spawnbase', 'spawnu', 'split_command_line', 'sys', 'utils', 'which']
二. pexpect使用详解
首先以开篇中描述的自动登录192.168.1.10服务器执行ifconfig命令查看网卡状态为例,
#vim test1.py
import pexpect #导入pexpect模块
child = pexpect.spawn('ssh user@192.168.1.10') #生成一个子程序,程序执行ssh登录操作
fout = file('test1.log','a') #定义一个日志文件
child.logfile = fout #将子程序child的过程输出到日志文件fout(也就是test1.log文件)
child.expect('password') #等待程序的输出,判断是否匹配字符串‘password’
child.sendline('1qaz!QAZ') #如果匹配,则发送密码给服务器验证,这里user的密码是1qaz!QAZ
child.expect('#') #等待上一步执行后输出,判断是否匹配字符串'#'
child.sendline('ifconfig') #如果匹配,则发送ifconfig命令
child.expect('#') #继续等待上步输出,判断是否匹配字符串'#'
print child.before #向标准输出打印子程序最后一次匹配前的内容
命令执行结果如下:
[root@localhost python]# python p6.py
ifconfig
eth0 Link encap:Ethernet HWaddr 00:0C:29:19:42:F2
inet6 addr: fe80::20c:29ff:fe19:42f2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:2437 errors:0 dropped:0 overruns:0 frame:0
TX packets:9 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:269529 (263.2 KiB) TX bytes:2310 (2.2 KiB)
eth1 Link encap:Ethernet HWaddr 00:0C:29:19:42:FC
inet addr:192.168.0.92 Bcast:192.168.3.255 Mask:255.255.252.0
inet6 addr: fe80::20c:29ff:fe19:42fc/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:120482 errors:0 dropped:0 overruns:0 frame:0
TX packets:61192 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:96175495 (91.7 MiB) TX bytes:7291914 (6.9 MiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:1883 errors:0 dropped:0 overruns:0 frame:0
TX packets:1883 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:258305 (252.2 KiB) TX bytes:258305 (252.2 KiB)
发现当前目录下多出一个test1.log文件,文件内容为:
[root@localhost python]# cat test1.log
user@192.168.1.10's password: 1qaz!QAZ
Last login: Thu Feb 25 19:35:51 2016 from 192.168.1.5
现在我们以上文示例多pexpect语法进行详细说明
spawn类
spawn作为pexpect的主要类接口,作用是启动和控制子程序,以下是它的构造函数
class pexpect.spawn(command, args=[],timeout=30,maxread=2000,searchwindowsize=None,logfile=None,cwd=None,env=None,ignore_sighup=Ture)
其中command为系统支持的任一命令,其他参数可选。
通过定义可以看到,这个类的作用就是执行一个系统命令用于开启自动化运行的第一步,所以通常放在第一行
注:这里说的子程序可以理解为就是spawn类的一个实例
2. 第一步执行后自动化脚本就开始运行了,如果想通过一个日志文件记录脚本在执行过程中经过了哪些步骤,以便了解细节及后期故障排查,则可以通过child.logfile方法指定一个日志输出文件。为了能够记录的完整,我们通常在启动脚本后随即就定义日志输出,也就是此命令放在第二行的原因。
3. expect方法
expect定义了一个子程序输出的匹配规则。
上例中child.expect('password') 意思就是在ssh user@192.168.1.10后判断程序是否会返回一个包含"password"的字符串,这里需要说明两点。1,引号中的字符串可以不是完整的程序返回,只要是程序返回的子集就可以匹配成功。比如程序返回的完整字符串是'user@192.168.1.10's password:',但是引号中只写'password'也是可以匹配成功的。2,如果预设的返回值如果不匹配,程序不会自动向下继续执行,而是卡在匹配失败的这个界面。比如在上例child.expect('password')前再加一条child.expect('nihao'),这时程序执行就停止了,查看日志文件可以发现最后一行为root@127.0.0.1's password:表示程序卡在输入密码这里了。
4. sendline方法
sendline作用是向子程序发送响应命令并按下回车,可以理解成替代了我们在标准键盘上的输入。上例中的意思就是在收到子程序返回字符串'password'后发送密码。
5. before和after方法
before保存了最近一次匹配成功前子程序的返回内容,after保存了最近一次匹配成功后子程序返回的内容。上例中最后一次匹配字符串是'#',在匹配成功前程序的返回是执行ifconfig命令的结果,所以print child.before会输出ifconfig命令结果到显示器。
上文描述了使用pexpect执行自动化操作的大致步骤,按此方法详细顺序定义好程序的返回和要输入内容即可完成大量操作的自动化执行,定义之前可以手工先执行一下命令确认具体的交互过程。