返回介绍

5.2 pexpect 的核心组件

发布于 2024-01-29 22:54:23 字数 5591 浏览 0 评论 0 收藏 0

下面介绍pexpect的几个核心组件包括spawn类、run函数及派生类pxssh等的定义及使用方法。

5.2.1 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 = 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.1.21')
fout = file('mylog.txt','w')
child.logfile = fout
#child.logfile = sys.stdout
child.expect("password:")
child.sendline("U3497DT32t")
child.expect('#')
child.sendline('ls /home')
child.expect('#')

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

# cat mylog.txt        
root@192.168.1.21's password: U3497DT32t
Last login: Tue Jan  7 23:05:30 2014 from 192.168.1.20
[root@SN2013-08-021 ~]# ls /home
ls /home
cc.py        poster-0.8.1            tarfile.tar.gz  zipfile.zip
default.tar.gz  poster-0.8.1.tar.gz        test.sh
dev         pypa-setuptools-c508be8585ab  zipfile1.zip

(1)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'])
输出: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方法有两个非常棒的成员:before与after。before成员保存了最近匹配成功之前的内容,after成员保存了最近匹配成功之后的内容。例如:

import pexpect
import sys
child = pexpect.spawn('ssh root@192.168.1.21')
fout = file('mylog.txt','w')
child.logfile = fout
child.expect(["password:"])
child.sendline("980405")
print "before:"+child.before
print "after:"+child.after

运行结果如下:

before:root@192.168.1.21's
after:password:

(2)read相关方法

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

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

5.2.2 run函数

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

参数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})

5.2.3 pxssh类

pxssh是pexpect的派生类,针对在ssh会话操作上再做一层封装,提供与基类更加直接的操作方法。

pxssh类定义:

class pexpect.pxssh.pxssh(timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, env=None)

pxssh常用的三个方法如下:

login建立ssh连接;

logout断开连接;

prompt等待系统提示符,用于等待命令执行结束。

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

【/home/test/pexpect/simple1.py】

import pxssh
import getpass
try:
  s = pxssh.pxssh   #创建pxssh对象s
  hostname = raw_input('hostname: ')
  username = raw_input('username: ')
  password = getpass.getpass('please input 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, e:
  print "pxssh failed on login."
  print str(e)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文