使用pexpect模块实现交互式脚本

宋文杰   2015-02-28 23:00:00

运维工作中为了减免工作量,经常要通过脚本解决交互式问题。能不能完美解决这个问题,直接决定了你是否可以升职加薪,出任cto,迎娶白富美,走向人生巅峰!

b0646704-2baa-44dd-ad60-52d3bbb175d9

什么是交互式问题?

你登陆远程主机,是不是需要由人来输入密码?使用分区命令分区,是不是由人来配置分区大小?想看电影,能不能自动查找路径并下载?好吧,发散一下你的思维,相信你一定懂了什么是交互式问题。

解决方法其实有很多,本章带来的python的pexpect模块,闲话少说,直接上code

安装过程如下:

通过easy_install 或者 pip进行安装

[root@flcdahom ~]# easy_install pexpect

无标题

常用的交互模型如下:

1

如果用以前shell的思考方法,通常关心两样东西:命令的输出,返回值。

(利用返回值可以判断命令是否执行成功。)

Pexpect中的run函数能够很好的帮我们完成这个任务

Code:

import pexpect
 
command_output, exitstatus = pexpect.run('ls -lh /etc/passwd', withexitstatus=1)
 
print "#############"
 
print "command_output: %s" % command_output
 
print "#############"
 
print "exitstatus: %s" % exit status

2

还能实现简单的交互,例如通过ssh在远程主机执行一条命令,通过pexpect自动帮你填充密码

Code:

import pexpect
 
command_output, exitstatus = pexpect.run("ssh ptest@localhost 'ls -lh /etc/passwd'", events={'(?i)pas
 
sword':'123456\n'}, withexitstatus=1)
 
print "#############"
 
print "command_output: %s" % command_output
 
print "#############"
 
print "exitstatus: %s" % exitstatus

4

5

如果不是特别复杂的交互,到这里基本就满足你的日常需要了。
接下来更深入的学习一下spawn,它能帮我们解决更加复杂的交互问题
先概念性的记住以下方法:
1 产生子进程执行相关命令pexpect.spawn() 返回一个子进程实例:

child = pexpect.spawn(‘command’)

2 该实例产生的日志信息可以通过child.logfile 进行配置:

f = file(’/tmp/plog.out’, ‘w’)

#f为一个文件对象

child.logfile = f

#通过logfile属性,设定子进程的log写入到f中
3 使用expect进行交互

index = child.expect([“命令执行成功输出的内容”,” 命令执行失败输出的内容” pexpect.EOF , pexpect.TIMEOUT])

expect方法中的参数为一个list,写入你程序执行之后屏幕显示的若干情况,然后按照该list的下标来进行比较,就能知道命令是否成功执行。
当index 为0代表出现了“命令执行成功输出的内容”,当命令为1时代表” 命令执行失败输出的内容”
最后的EOF,TIMEOUT为默认定义好的两个异常,一个
pexpect.EOF 代表子进程结束,pexpect.TIMEOUT 代表超时——默认30s。
4 通过sendline给子进程发送信息,自动带一个回车:

child.sendline(‘command’)

当你利用上面的index判断到程序执行的不同情况时,利用send发送命令来进行交换。
#甚至可以通过sendcontrol(‘c’)来发送ctrl+c
5 使用try,except来进行异常处理

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

这段代码就是在前面的基础上,利用try,except 在不同情况执行不同操作,使你的程序看起来更加专业。

注意:这不是必须的。
6 可以使用before来打印命令执行结果:

p = pexpect.spawn('/bin/ls')
p.expect(pexpect.EOF)
print p.before

当你需要打印前面命令的执行输出时,就可以这么做,当然这也不是必须的。
以上6点我觉得可以让你游刃有余的解决日常90%进程交互的问题。
这里share 一个经验就是写程序之前,先把这6个问题思考好,定义好,再去写程序的主题就ok了!
下面来一个小的需求来演练一下
Code:

import pexpect
 
username = 'ptest'
 
key = '123456'
 
f = file('/tmp/plog.out', 'w')
 
if __name__ == "__main__":
 
   child = pexpect.spawn('ftp localhost')
 
   child.logfile = f
 
   try:
 
       index = child.expect(['Name'])
 
       if index == 0:
 
           child.sendline(username)
 
           index = child.expect(['Password'])
 
           if index == 0:
 
               child.sendline(key)
 
               index = child.expect(['Login successful.*ftp>'])
 
               if index == 0:
 
                   child.sendline('ls')
 
                    index = child.expect(['test.mp3'])
 
                   if index == 0:
 
                       child.sendline('bin')
 
                       child.sendline('get test.mp3')
 
                       index = child.expect(['Transfer complete.*ftp>'])
 
                       if index == 0:
 
                           print 'download complete!'
 
                           child.sendline('bye')
 
   except:
 
       print child.before
 
       print 'see /tmp/plog.out can find more info!'

执行一下这段代码,然后去查看一下/tmp/plog.out文件的内容
这段代码虽然非常丑陋,但还是能起到练习的作用,稍加变化就能满足您生产需求!~

注:此文章转自天使团隋老大,仅供参考与学习,原文链接如下:

http://www.suilaoda.com/2014/11/by-pexpect-module-for-interactive-script.html

 

回贴

沪ICP备15046442号
蝉知1.6