pexpect 的核心组件
下面介绍 pexpect 的几个核心组件包括 spawn 类、run 函数及派生类 pxssh 等的定义及使用方法。
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:302014 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
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("defore:" + child.before)
print("after:" + child.after)
运行结果如下:
defore:root@192.168.1.21's
after:password:
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)
使用 fun 函数实现如下,是不是更加简洁、精炼了?
from pexpect import *
run('scp foo user@example.com:.', events={'(?i)password': mypassword})
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() 方法断开连接。
Unresolved include directive in modules/ROOT/pages/section02/ch05/ch5-02.adoc - include::example$/第五章/pexpect/simple1.py[]