一 点睛
pexpect可以理解成Linux下的expect的Python封装,通过pexpect我们可以实现对ssh、ftp、passwd、telnet等命令行进行自动交互,而无需人工干涉来达到自动化的目的。比如我们可以模拟一个FTP登录时的所有交互,包括输入主机地址、用户名、密码、上传文件等,待出现异常我们还可以进行尝试自动处理。
pexpect的官网地址: http://pexpect.readthedocs.org/en/latest
二 安装
[root@localhost /]# pip install pexpect
三 spawn类
1 点睛
spawn是pexpect的主要类接口,功能是启动和控制子应用程序
2 构造函数
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 = file('mylog.txt','w')
child.logfile = fout
输出到标准输出的方法如下
child = pexpect.spawn('some_command')
child.logfile = sys.stdout
下面为一个完整的示例,实现远程SSH登录,登录成功后显示/home目录文件清单,并通过日志文件记录所有的输入与输出
import pexpect
import sys
child = pexpect.spawn('ssh root@192.168.0.104')
fout = file('mylog.txt','w')
child.logfile = fout
#child.logfile = sys.stdout
child.expect('(yes/no)?')
#Are you sure you want to continue connecting (yes/no)?
child.sendline("yes")
child.expect('password: ')
child.sendline("123456")
child.expect('#')
child.sendline('ls /home')
child.expect('#')
下面是mylog.txt文件输出结果,可以看到pexpect产生的全部输入与输出信息。
[root@localhost pymaintain]# cat mylog.txt
yes
yes
\S
Kernel \r on an \m
root@192.168.0.104's password: 123456
Last login: Sun Feb 24 13:22:36 2019 from 192.168.0.120
hello cakin24!
[root@slave2 ~]# ls /home
ls /home
123 av jt jzmb lw sdf tony user1 user3 user4
3 expect方法
expect定义了一个子程序输出的匹配规则。
方法定义:expect(pattern,timeout=–1,searchwindowsize=–1)
其中,参数pattern表示字符串、pexpect.EOF(指向缓冲区尾部, 无匹配项)、pexpect.TIMEOUT(匹配等待超时)、正则表达式或者前面四种类型组成的列表(List),当pattern为一个列表时,且不止一个列表元素被匹配,则返回的结果是子程序输出最先出现的那个元素,或者是列表最左边的元素(最小索引ID)
import pexpect
child = pexpect.spawn("echo 'foobar'")
print child.expect(['bar','foo','foobar'])
输出结果表示foo被匹配
[root@localhost pymaintain]# python 5_2_2.py
1
参数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方法有两个非常棒的成员:before与after。before成员保存了最近匹配成功之前的内容,after成员保存了最近匹配成功之后的内容。
示例:
import pexpect
import sys
child = pexpect.spawn('ssh root@192.168.0.104')
fout = file('mylog.txt','w')
#child.logfile = fout
child.logfile = sys.stdout
child.expect('(yes/no)?')
#Are you sure you want to continue connecting (yes/no)?
child.sendline("yes")
child.expect('password: ')
child.sendline("123456")
print "+++++++++++++++"
print "before:"+child.before
print "after:"+child.after
print "+++++++++++++++"
child.expect('#')
child.sendline('ls /home')
child.expect('#')
输出结果:
[root@localhost pymaintain]# python 5_2_1.py
yes
yes
\S
Kernel \r on an \m
root@192.168.0.104's password: 123456
+++++++++++++++
before:yes
\S
Kernel \r on an \m
root@192.168.0.104's
after:password:
+++++++++++++++
Last login: Sun Feb 24 13:49:29 2019 from 192.168.0.120
hello cakin24!
4 read相关方法
下面这些输入方法的作用都是向子程序发送响应命令,可以理解成代替了我们的标准输入键盘。
send(self, s) # 发送命令,不回车
sendline(self, s='') # 发送命令,回车
sendcontrol(self, char) # 发送控制字符,如child.sendcontrol('c')等价于”ctrl+c”
sendeof() # 发送eof