
概述
交互式程序通常需要用户手动完成一些操作,因此常常会成为系统管理自动化和测试自动化中的障碍。最早出现在 Unix 上的 Expect 语言可以用来和 passwd/ssh/telnet/ftp 等命令行程序进行交互,将用户从这些手工操作中解放出来。作为 Tcl 语言的扩展,Expect 最初由 Tcl 编写,但是现在已经有了 Perl 和 Python 的实现。Perl 作为最为流行的脚本语言之一,整合了 C/sh/sed/awk 的优点且和系统结合紧密,已成为系统管理员有力的工具。本文将介绍 Perl 中的 Expect 和 Expect::Simple 两个模块,结合系统管理和软件测试实例,说明如何实现与命令行程序的自动化交互。
局限性
Perl 的 Expect 模块依赖于 IO::Tty,而 IO::Tty 只适用于 POSIX 兼容系统,所以该模块目前还无法直接在 Windows 环境下使用。有两个变通的方法:一是使用 Cygwin 虚拟机;二是基于 ActiveState 提供的 Expect for Windows 工具,使用 Tcl 语言编写自动化脚本。本文所使用的环境为 RHEL5.3 32 位版本,Perl 5.8.8。
下载和安装
Expect 和 Expect::Simple 都可以从 CPAN 网站上直接下载,最新版本为 Expect-1.21 和 Expect::Simple-0.04。比较方便的安装方法是使用 Perl 自带的包管理工具 cpan:
清单 1. 模块安装
perl -MCPAN -e 'install Expect::Simple'
请确保有足够的执行权限,cpan 将自动解决模块间依赖关系。由于 Expect::Simple 依赖于 Expect,安装 Expect::Simple 的过程中,Expect 模块以及另外的依赖模块都会自动安装完毕。
Expect 模块详解
与最初的 Expect 语言类似,Expect 模块的主要功能也通过 spawn,expect,send 三个方法实现。
spawn:启动目标程序
清单 2. spawn 方法原型
$obj = Expect->spawn( $command, @parameters );
spawn 是 Expect 类的主要方法之一,通过 fork 和 exec 启动目标程序,若成功则返回 Expect 对象,否则返回 undef。参数 $command 指定目标程序,@parameters 为可选参数。下面是一个简单的例子:
清单 3. spawn 用法示例
$obj1 = Expect->spawn( "ftp 9.9.9.9" ); # 启动 ftp 进程 $obj2 = Expect->spawn( "ftp", "9.9.9.9" ); # 与上一行等效
月季品种的差别极其细微,观察者要从客观实际出发,仔细认真地从宏观和微观两个不同角度进行观察,比较它们的不同点,比如株龄相同,其高度、粗壮程度、生长势却有差别。 //首先创建批处理文件,将notepad命令写入这个批处理文件中,">"是一个重定向命令,意思是将这个符号前面的字符串写入这个符号后面的文件中,如果这个文件已经存在,则会清空原有数据,再将文本写入第一行。在向此寄存器写入轴设定字和命令自后,它将立即执行,某些命令在写入wr0之前应先写入wr6和wr7。
注:在 spawn 的实现中,$command 和 @parameters 都原封不动地传递给了 Perl 的 exec 函数。根据 exec 函数的说明文档,如果传递进来的是多元列表参数,exec 直接将其传递给 execvp 系统调用;如果传递进来的是标量参数或者单元列表参数,exec 函数将检查是否存在 shell 元字符 ( 如 | & ; ( ) < > 等 ),若存在,则将此参数交给系统 shell 进行解析,否则将其分词后传递给 execvp 系统调用。因此如果 spawn 的是一个含有 shell 元字符的复合命令,我们一般只能将其完整写入 $command。
expect:等待特定输出
清单 4. expect 方法原型
$obj->expect( $timeout, @match_patterns );
使用 Expect 对象的 expect 方法等待目标程序的特定输出。参数列表中 $timeout 设定超时 ( 以秒为单位 ),@match_patterns 提供一个或多个匹配模式,如果在设定时间内目标程序输出结果和 @match_patterns 中某元素匹配则成功返回。缺省情况下 expect 使用精确匹配,若想使用正则表达式,可以在该模式元素前加 '-re' 前缀 :

清单 5. 启用正则表达式匹配
$obj->expect( 10, 'match me exactly', '-re'=>'match/s+me/s+exactly' );
标量上下文中 expect 返回匹配模式在 @match_patterns 中的位置 ( 注意下标从 1 开始 ),若不成功则返回 undef。而列表上下文中 expect 返回一个包含详细匹配信息的列表:
清单 6. expect 方法的列表返回值
( $pos, $err, $match, $before, $after ) = $obj->expect( $timeout, @patterns );
其中 $pos 就是在标量环境中的返回值,$err 是出错信息,$match 为成功匹配的字串,$before 为匹配字串之前的输出部分,$after 为匹配字串之后的输出部分。下面这个例子 (matchinfo.pl 及其输出结果 ) 具体说明了这些值的含义:
清单 7. matchinfo.pl
#! /usr/bin/perl
# 9.125.13.44 是一台 ftp 服务器,使用 ftp 命令登录时的输出为:
# Connected to 9.125.13.44.
# 220 AIX6144 FTP server (Version 4.2 Tue Dec 22 14:13:26 CST 2009) ready.
# 502 authentication type cannot be set to GSSAPI
# 502 authentication type cannot be set to KERBEROS_V4
# KERBEROS_V4 rejected as an authentication type
# Name (9.125.13.44:root):
use Expect;
$obj = Expect->spawn( "ftp 9.125.13.44" ) or die "Couldn't spawn ftp, $!";
# 关闭目标程序的输出显示
$obj->log_stdout( 0 );
# 使用 slice 获取列表环境下的返回值
@result{ "position", "error", "match", "before", "after" } = $obj->expect( 10, 'Name' );
# 查看匹配结果
print "$_ = $result{$_}/n" foreach ( keys %result );
# 关闭目标程序
$obj->soft_close( );
我正在编写一个程序,想外调一个现成的com,但是又不想显示其产生的窗口,我试过可以关闭该窗口,一样能运行,可是在我的程序中如何获取该窗口句柄,如何自动关闭它。hkcmd.exe此进程是可以在进程上关闭禁用该进程的,对系统不会造成影响,打算呢会导致显示配置程序的热键出现失效情况,如果已经关闭了该进程出现热键失效,可以直接在“开始-运行”的运行输入框上输入“hkcmd”命令即可运行启用该程序。不过 在使用上需要 注意几点就是 先进行 编译 然后运行 并且 控制台程序 和 图形模式的 编译方式不同 需要 分别点 两个不同的 编译按钮 左侧的是控制台按钮 右侧的是 图形模式编译按钮 通行模式编译按钮 是 运行 然后是 带参数的运行再就是 运行捕获 (会在下方的输出面板上面显示 程序运行的时候输出的文字内容)。
清单 8. matchinfo.pl 输出结果
after = (9.125.13.44:root): match = Name error = position = 1 before = Connected to 9.125.13.44. 220 AIX6144 FTP server (Version 4.2 Tue Dec 22 14:13:26 CST 2009) ready. 502 authentication type cannot be set to GSSAPI 502 authentication type cannot be set to KERBEROS_V4 KERBEROS_V4 rejected as an authentication type
Expect 对象也提供了单独的方法来获得这些匹配信息,如 $obj->before( ),$obj->after( ),$obj->match( ),$obj->error( ) 等。
使用 -i 选项可以对多个或多组 Expect 对象同时执行 expect 操作:
清单 9. -i 选项
$obj1 = Expect->spawn( "ftp 9.125.13.44" ) or die $!;
$obj2 = Expect->spawn( "telnet 9.9.9.9" ) or die $!;
$obj3 = Expect->spawn( "ssh 9.181.59.64" ) or die $!;
expect ( $timeout,
'-i', $obj1, '-re', qr/name/i,
'-i', [$obj2, $obj3], '-re', qr/login/i, '-re', qr/password/i,
)
f=inline('函数表达式','变量1','变量2',...) y=f(实参列表) 实参列表应与定义时的变量顺序保持一致 例: 方式三:内联函数和匿名函数 内联函数 调用方式 f=inline('x^2+y','x','y') y=f(2,3) 根据实际情况,定义函数时可能需要使用数组运算 例: 方式三:内联函数和匿名函数 匿名函数 + 函数句柄 f = @ (变量列表) 表达式 y=f(实参列表) 调用方式 f = @(x,y) x^2 + y。并且指向该构造函数,当构造函数没有返回值时,或者不存在return语句时,this指向是绑定到该新创建出来的实例对象上的,构造函数的返回值就等于构造器函数的调用,return 后面的值,等于new 构造器函数()执行的结果,所以说我当时在构造函数中返回值设置成了一数组,那么该构造函数new personmessage()返回的是数组对象,于是会person实例化出来的对象便会继承array.prototype.construcor = array。比如像列表,字典这样的对象是通过引用传递、和c语言里的用指针传递数组很相似,可变对象能在函数内部改变。
此外在$obj->expect($timeout, @match_patterns)中,@match_patterns 还可采用[ $regexp, sub{}, @opt_params ]的形式,根据不同模式来执行不同后续操作。逻辑流程较为简单时,利用此特点可以使代码组织更加紧凑。下面的 telnet 登录脚本 (exptelnet.pl) 采用了这种形式:
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-118246-1.html
最后一笑超赞哎
本来就应该受到法律的制裁