如何在expect脚本中php调用shell脚本系统命令

expect脚本中如何调用另一个脚本?
[问题点数:30分]
expect脚本中如何调用另一个脚本?
[问题点数:30分]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
相关帖子推荐:
2007年9月 Linux/Unix社区大版内专家分月排行榜第二2007年7月 Linux/Unix社区大版内专家分月排行榜第二
2010年6月 Linux/Unix社区大版内专家分月排行榜第三2008年4月 Linux/Unix社区大版内专家分月排行榜第三2008年3月 Linux/Unix社区大版内专家分月排行榜第三
2007年6月 Linux/Unix社区大版内专家分月排行榜第二2006年12月 Linux/Unix社区大版内专家分月排行榜第二2006年7月 Linux/Unix社区大版内专家分月排行榜第二2006年5月 Linux/Unix社区大版内专家分月排行榜第二2006年4月 Linux/Unix社区大版内专家分月排行榜第二2006年3月 Linux/Unix社区大版内专家分月排行榜第二2006年2月 Linux/Unix社区大版内专家分月排行榜第二2006年1月 Linux/Unix社区大版内专家分月排行榜第二2002年11月 Linux/Unix社区大版内专家分月排行榜第二
2008年9月 Linux/Unix社区大版内专家分月排行榜第三2006年8月 Linux/Unix社区大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
单击提交则表示您同意developerWorks
的条款和条件。 .
所有提交的信息确保安全。
developerWorks 社区:
我的概要信息
选择语言:
Expect 工具用于实现交互式命令行程序的自动化执行,本文将介绍 CPAN 上与之相关的两个模块 Expect 和 Expect::Simple,结合实例讲解其用法及注意事项。灵活使用这些工具将有助于提高系统管理员和测试人员的工作效率。
, 软件工程师, IBM 中国软件开发中心
袁麟,软件工程师,2009 年加入 IBM 中国开发中心,关注软件全球化与本地化、测试自动化等领域,目前主要从事 Tivoli Storage 产品的全球化测试工作。
概述交互式程序通常需要用户手动完成一些操作,因此常常会成为系统管理自动化和测试自动化中的障碍。最早出现在 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" );
# 与上一行等效上述两行执行结果相同,但其实际处理过程存在细微差别。一般情况下我们可以把完整的命令行 ( 甚至可以是复合命令,包括命令、参数、管道符、重定向符等 ) 都写入 $command 而不指定 @parameters。注:在 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):
$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( );为了使运行结果更加清楚,示例中使用 $obj-&log_stdout(0)关闭目标程序的输出显示。该程序的运行结果为:清单 8. matchinfo.pl 输出结果 after =
(9.125.13.44:root):
match = Name
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 typeExpect 对象也提供了单独的方法来获得这些匹配信息,如 $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,
)本例使用函数风格的 expect 方法 ( 区别于通过对象调用 ),构造匿名数组的引用 [...] 来传递多个 Expect 对象。此外在 $obj-&expect($timeout, @match_patterns)中,@match_patterns 还可采用 [ $regexp, sub{}, @opt_params ]的形式,根据不同模式来执行不同后续操作。逻辑流程较为简单时,利用此特点可以使代码组织更加紧凑。下面的 telnet 登录脚本 (exptelnet.pl) 采用了这种形式:清单 10. exptelnet.pl #! /usr/bin/perl
my $PROMPT = '[\]\$\&\#]\s*$'; # 远程系统的命令提示符模式
$obj = Expect-&spawn( "telnet 9.125.13.44" ) or die "Couldn't spawn telnet, $!";
$obj-&expect( 10,
[ qr/login:\s*$/i,
sub{ my $self = $self-&send( "root\r" ); exp_}
[ qr/password:\s*$/i,
sub{ my $self = $self-&send( "zu88jie\r" ); exp_}
[ qr/$PROMPT/,
sub{my $self= $self-&send( "logout\r" ); exp_continue_}
);示例中匿名函数返回 exp_continue 符号将重置等待时间并继续执行 expect,使得一次 expect 调用可以完成多次匹配动作。与之相对的是返回 exp_continue_timeout 符号,在继续执行 expect 时不重置等待时间。send:发送数据清单 11. send 方法原型 $obj-&send( @strings );当交互式程序等待用户输入时,可以使用 send 方法向其提供输入数据。需要注意,send 送出的数据可能会回显在终端上 ( 具体与终端设置有关 ),此数据会进入 Expect 对象的匹配缓冲区,被下一个 expect 动作接收。为了避免 send 数据对 expect 匹配造成混乱,一般可以使用 $obj-&stty( "-echo" )方法关闭终端回显,或者在 spawn 前使用 $obj-&raw_pty(1)将终端设定成 raw 模式 (raw 模式将关闭回显,禁止回车 - 换行符翻译 ),或者为 expect 提供更加精确的匹配模式。log_file:设置日志记录清单 12. log_file 方法原型 $obj-&log_file( $filename | $filehandle | undef );将交互过程的内容输出到日志文件能便于自动化脚本的追踪和调试。通常使用文件名或者文件句柄作为参数来指定具体日志文件,例如:清单 13. 设置日志记录 $obj = Expect-&spawn( "ftp 9.9.9.9" );
# 使用"w"选项截断旧日志
$obj-&log_file( "./out.log", "w" );debug:设置调试信息清单 14. debug 方法原型 $obj-&debug( 0 | 1 | 2 | 3 );参数 0 为禁止调试信息 ( 缺省值 ),1 ~ 3 级详细程度递增。另一个相关的方法是 $object-&exp_internal( 1 | 0 ),用以打开或关闭 expect 对象的内部调试信息。将此标志设置为 1 可以看到 expect 对象对输出内容尝试匹配的完整过程 (expdebug.pl):清单 15. expdebug.pl #! /usr/bin/Perl
$obj = Expect-&spawn( "ftp 9.125.13.44" ) or die $!;
$obj-&exp_internal( 1 ); # 打开对象内部调试信息
$obj-&expect( 10, "Name" );
$obj-&soft_close( );运行结果为:清单 16. expdebug.pl 输出详细匹配过程 Starting EXPECT pattern matching...
... 省略中间输出 ...
spawn id(3): Does `'
pattern #1: -ex `Name'? No.
... 省略中间输出 ...
pattern #1: -ex `Name'? YES!!
Before match string: `Connected to 9.125.13.44.\r\n220 AIX6144 FTP server ...'
Match string: `Name'
After match string: ` (9.125.13.44:root): '
Matchlist: ()从结果中可以监视目标程序每一条输出与 expect 模式的匹配情况,若匹配成功还能查看 before\match\after 字串,这对于调试程序大有帮助。interact:返回交互模式清单 17. interact 方法原型 $obj-&interact( );为了适应某些特殊场合,我们可能需要将控制权交还给目标程序,此时只需使用 $obj-&interact( )方法。clear_accum 与 set_accum:操纵匹配缓冲区清单 18. clear_accum 与 set_accum 方法原型 $obj-&clear_accum( );
$obj-&set_accum( $value );expect 方法针对 Expect 对象的匹配缓冲区 (accumulator) 进行匹配尝试,默认情况下每次匹配成功后,accumulator 中 before 和 match 部分将被清除,下次匹配从 after 开始。但是 Expect 对象提供了 clear_accum 与 set_accum 方法改变这种行为:使用 $obj&set_accum( $value )将缓冲区内容设置成 $value,使用 $obj&clear_accum( )清空缓冲区内容。具体用法参见如下代码片段:清单 19. 操纵匹配缓冲区内容 $obj-&notransfer( 1 );
$obj-&expect( $timeout,
# 1 保留 accumulator 内容 , pattern1 将被再次匹配
[ "pattern1",
sub { my $self = ... }
# 2 将 accumulator 内容设置为 after string,即截断 before 和 match string
[ "pattern2",
sub { my $self = ...; $self-&set_accum( $self-&after( ) );}
# 3 将 accumulator 内容清空
[ "pattern3",
sub { my $self = ...; $self-&clear_accum( );}
);示例中 $obj-&notransfer( 0 | 1 )方法用于设置是否保留匹配缓冲区内容。0 是默认行为 ( 清除 before 和 match);1 为保留所有内容。保留缓冲区内所有内容会导致原先匹配过的模式被再次匹配 (#1)。我们也可手动设置 accumulator 内容来影响下一次匹配 (#2 #3)。Expect::Simple 模块详解Expect::Simple 对 Expect 模块进行了封装,隐藏其内部复杂机制。此模块处理一些简单的应用已经足够。new;构造方法清单 20. new 方法原型 $obj = Expect::Simple-&new( \%attr );创建 Expect::Simple 对象的同时启动目标程序,需传递关联数组 %attr 的引用作为参数。通过该关联数组设置目标程序的相关信息 ( 如命令、超时、提示符等 ),因此该数组必须包含 Prompt,DisconnectCmd,Cmd 等键值。Cmd 键指定目标程序及其参数。它的值可以是标量或者是数组引用:Cmd =& $command或 Cmd =& [ $command, $arg1, $arg2, ...]。在 Expect::Simple 的实现中 $command 或 $command, $arg1, $arg2, ...都被直接传给 Expect 的 spawn 方法,因此前面对 spawn 方法的分析在这里同样适用:可以把完整的命令行写入 $command;对不含 shell 元字符的简单命令,也可以使用分拆形式,传递数组引用。Prompt 键指定一个或一组预期的输入提示 ( 支持正则表达式 ):Prompt =& ['ftp&', 'telnet&', -re =& 'prompt\d+&\s+']。Prompt 键值相当于 Expect 模块中 expect 方法的匹配模式参数。DisconnectCmd 键指定退出目标程序所用的指令:DisconnectCmd =& 'exit'。Timeout 键设定超时 ( 缺省值为 1000 秒 ),如果目标程序在设定时间内未响应则返回。Expect::Simple 在目标程序启动之前设置超时且使用全局设定,因此无法区分处理目标程序执行过程中立即响应部分和有明显延时的部分。在设定超时值时需要考虑响应最慢的阶段。RawPty 键用于设置终端,对应于 Expect 模块的 raw_pty 方法,默认值为 0。Verbose 键用于设定输出内容的详细程度:Verbose =& 3。send:顺序发送数据使用 $obj-&send( $cmd | @cmds)向目标程序发送一条或依次发送多条数据。每条数据送达后等待下一个输入提示以发送下一条数据。以 ssh 自动登录为例,serialsend.pl 演示如何顺序发送多条数据:清单 21. serialsend.pl #! /usr/bin/perl
use Expect::S
my %attr = (
=& [ -re =& qr/password:\s*$/i,
-re =& qr/[\]\$\&\#]\s*$/ ],
=& 'ssh root@9.125.13.44',
DisconnectCmd
=& 'exit',
my $obj = Expect::Simple-&new( \%attr );
# 自动输入密码,执行 ls 命令,退出。由于已设定退出命令,此处不必再发送 exit
$obj-&send( "zhu88jie", "ls" );输出结果:清单 21. serialsend.pl 执行结果 Running command...done.
Sending `zhu88jie'
Sending `ls'
Disconnecting.若想看到 ls 命令的执行输出,只需将 Verbose 值设为 4。before,after,match_str,error:查看匹配信息这些方法和 Expect 模块中的 before,after,match,error 一一对应,不再详细阐述。需要注意由于 Expect 对象的缓冲区在匹配过程中会不断更新,在依次发送多条数据的情况下,使用这些方法只能查看最近一次匹配结果。expect_handle:使用内部对象Expect::Simple 封装了 Expect 模块的细节,提供简单易用的接口,但是当 Expect::Simple 提供的功能无法以满足需求时,还可以使用这个方法直接操作内部 Expect 对象。应用示例Expect 模块使用示例本例 (ftpdemo.pl) 演示如何使用 Expect 模块从 ftp 上自动下载文件。清单 22. ftpdemo.pl #!/usr/bin/perl
# Usage: ftpdemo.pl [-u username] [-p password] host file1 [file2 file3 ...]
use Getopt::S
# 设置缺省用户名和密码
my %opts = ( u=&'anonymous', p=&'' );
# 解析 -u 和 -p 选项
getopt( 'up', \%opts );
$host = shift @ARGV;
# 下一个参数是 ftp 服务器地址
@files = @ARGV;
# 余下的参数为需要下载的文件
# 启动 ftp 进程
print "Starting ftp session with server $host ...\n";
$ftp = Expect-&spawn( "ftp $host" ) or die "Couldn't spawn ftp, $!";
# 屏蔽多余输出
$ftp-&log_stdout( 0 );
# 等待用户名输入提示
unless ( $ftp-&expect(30, -re=&qr/name \(.*?\):\s*$/i) ) {
die "Never got username prompt on $host, ".$ftp-&error( )."\n";
# 发送用户名数据
print "Sending username ($opts{u}) ... \n";
$ftp-&send( "$opts{u}\r" );
# 等待密码输入提示
unless ( $ftp-&expect( 30, -re=&qr/password:\s*$/i ) ) {
die "Never got password prompt on $hostname, ".$ftp-&error( )."\n";
# 发送密码
print "Sending password ( $opts{p} ) ... \n";
$ftp-&send( "$opts{p}\r" );
# 等待 ftp 命令行提示
unless ( $ftp-&expect(30,"ftp&") ) {
die "Never got ftp prompt after sending username, ".$ftp-&error( )."\n";
# 下载文件
foreach my $file ( @files ) {
print "Getting the $file ... \n";
$ftp-&send( "get $file\r" );
unless ( $ftp-&expect( 30,"ftp& " ) ) {
die "Never got ftp prompt after attempting to get $file, ".$ftp-&error( )."\n";
# 断开 ftp 连接
print "Download finished. Disconnecting ... \n";
$ftp-&send( "bye\r" );
$ftp-&soft_close( );
print "Done.\n";使用此脚本从 9.125.13.44 的 ftp 根目录下载 diskusage.log 文件,运行结果:清单 23. ftpdemo.pl 执行结果 # ./ftpdemo.pl -u root -p zhu88jie 9.125.13.44 diskusage.log
Starting ftp session with server 9.125.13.44 ...
Sending username (root) ...
Sending password (zhu88jie) ...
Getting the diskusage.log ...
Download finished. Disconnecting ...
#Expect::Simple 模块使用示例本例演示如何使用 Expect::Simple 对目标程序的输入输出进行测试。脚本 target.pl 作为此例中的目标程序。target.pl 循环读取用户输入,直到用户输入 quit 时退出。清单 24. target.pl #! /usr/bin/perl
my $p = 'tpt %d& ';
# 输入提示符
printf( $p, 0 );
while( && ){
do { print "byebye\n"; last } if /quit/;
# 如果用户输入 quit 则输出"byebye",跳出循环
print uc( $_ );
# 否则以大写的形式输出用户本次输入
printf( $p, $. );
# 更新输入提示符,$. 为读入行数计数器
print "quit& \n";
# 打印结束输入提示符某次运行结果如下:清单 25. target.pl 执行结果示例 # ./target.pl
tpt 2& quit
#exptest.pl 脚本用来模拟用户输入不同数据以测试目标程序的功能:清单 26. exptest.pl #! /usr/bin/perl
use Test::More tests =& 4; # 计划执行的测试用例总数
use Expect::S
my %attr = ( Cmd =& "./target.pl"
DisconnectCmd =& 'quit',
Prompt =& [ -re =& 'tpt\s\d+& ',
'quit& '],
RawPty =& 1,
Verbose =&3,
# 实际输出结果
my $obj = Expect::Simple-&new( \%attr );
# 测试用例 1,用户输入 a,预期目标程序输出 A
$obj-&send( 'a' );
chomp( $res = $obj-&before );
is( $res, 'A', 'Test case 1: Input "a" -& Output "A"' );
# 测试用例 2,用户输入 b,预期目标程序输出 B
$obj-&send( 'b' );
chomp( $res = $obj-&before );
is( $res, 'B', 'Test case 2: Input "b" -& Output "B"' );
# 测试用例 3,用户输入 quit,预期目标程序输出 byebye
$obj-&send( 'quit' );
chomp( $res = $obj-&before );
is( $res, 'byebye', 'Test case 3: Input "quit" -& Output "byebye"' );
# 测试用例 4,目标程序输出 byebye 后应直接输出 quit&
is ( $obj-&match_str, 'quit& ', 'Test case 4: Output "byebye" -& Output "quit&" ' );程序引用了 Test::More 模块,Test::More 是一个编写测试脚本的框架,这里只介绍程序中涉及的部分,更多内容请查阅 CPAN 网站上相关信息。整个测试过程使用 is 函数来判断用例的执行结果:is ( $got, $expected, $comments )。该函数判断 $got 与 $expected 是否相等,相等则通过,输出“ok”,否则输出“not ok”。在 target.pl 循环执行的过程中,如果用户未输入 quit,则每次 expect 都匹配到输入提示符“tpt\s\d+&”上,而终端回显又被关闭,因此匹配缓冲区的 before 部分即为目标程序上次输出的结果。程序中使用 $res 变量获得目标程序的实际输出,与预期结果比较判断测试用例是否通过。exptest.pl 实际执行的结果为:清单 27.exptest.pl 执行结果 # ./exptest.pl
Running command...done.
Sending `a'
ok 1 - Test case 1: Input "a" -& Output "A"
Sending `b'
ok 2 - Test case 2: Input "b" -& Output "B"
Sending `quit'
ok 3 - Test case 3: Input "quit" -& Output "byebye"
ok 4 - Test case 4: Output "byebye" -& Output "quit&"
Disconnecting.
访问 ,了解更多关于 Perl 的信息。
,可以使你快速入门。
访问 ,了解 Expect 的历史、发展和应用。
访问 CPAN 中 ,进行更加深入的研究。
访问 CPAN 中 ,进行更加深入的研究。
访问 CPAN 中 ,以便进一步了解。
在 中查阅跟多相关材料。
随时关注 developerWorks 和。 欢迎加入 。
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
IBM PureSystems(TM) 系列解决方案是一个专家集成系统
通过学习路线图系统掌握软件开发技能
软件下载、试用版及云计算
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Linux, Open sourceArticleID=600485ArticleTitle=使用 Perl 脚本实现交互式命令行程序的管理与测试自动化publish-date=Shell脚本学习之expect命令
第2页_Linux编程_Linux公社-Linux系统门户网站
你好,游客
Shell脚本学习之expect命令
来源:Linux社区&
作者:leexide
三、Expect工作原理
从最简单的层次来说,Expect的工作方式象一个通用化的Chat脚本工具。Chat脚本最早用于UUCP网络内,以用来实现计算机之间需要建立连接时进行特定的登录会话的自动化。
Chat脚本由一系列expect-send对组成:expect等待输出中输出特定的字符,通常是一个提示符,然后发送特定的响应。例如下面的 Chat脚本实现等待标准输出出现Login:字符串,然后发送somebody作为用户名;然后等待Password:提示符,并发出响应 sillyme。
Login: somebody Password: sillyme
Expect最简单的脚本操作模式本质上和Chat脚本工作模式是一样的。
1、实现功能
下面我们分析一个响应chsh命令的脚本。我们首先回顾一下这个交互命令的格式。
假设我们要为用户chavez改变登录脚本,要求实现的命令交互过程如下:
# chsh chavez Changing the login shell for chavez Enter the new value, or press return for the default Login Shell [/bin/bash]: /bin/tcsh #
可以看到该命令首先输出若干行提示信息并且提示输入用户新的登录shell。我们必须在提示信息后面输入用户的登录shell或者直接回车不修改登录shell。
2、实现自动执行
#!/usr/bin/expect# Change a login shell to tcsh
set user [lindex $argv 0]spawn chsh $userexpect "]:"send "/bin/tcsh " expect eof
(1)首行指定用来执行该脚本的命令程序,这里是/usr/bin/expect。
(2)程序第一行用来获得脚本的执行参数(其保存在数组$argv中,从0号开始是参数),并将其保存到变量user中。
(3)第二个参数使用expect的spawn命令来启动脚本和命令的会话,这里启动的是chsh命令,实际上命令是以衍生子进程的方式来运行的。
(4)随后的expect和send命令用来实现交互过程。脚本首先等待输出中出现]:字符串,一旦在输出中出现chsh输出到的特征字符串(一般特征 字符串往往是等待输入的最后的提示符的特征信息)。对于其他不匹配的信息则会完全忽略。当脚本得到特征字符串时,expect将发送/bin/tcsh和 一个回车符给chsh命令。最后脚本等待命令退出(chsh结束),一旦接收到标识子进程已经结束的eof字符,expect脚本也就退出结束。
3、决定如何响应
系统管理员往往有这样的需求,希望根据当前的具体情况来以不同的方式对一个命令进行响应。我们可以通过后面的例子看到expect可以实现非常复杂的条件响应,而仅仅通过简单的修改预处理脚本就可以实现。
下面的例子是一个更复杂的expect-send例子:
expect -re "\[(.*)]:"if {$expect_out(1,string)!="/bin/tcsh"} {send "/bin/tcsh" }send " "expect eof
(1)第一个expect命令现在使用了-re参数,这个参数表示指定的的字符串是一个正则表达式,而不是一个普通的字符串。对于上面这个例子里是查找一个左方括号字符(其必须进行三次逃逸(escape),因此有三个符号,因为它对于expect和正则表达时来说都是特殊字符)后面跟有零个或多个字符,最后是一个右方括号字符。这里.*表示表示一个或多个任意字符,将其存放在()中是因为将匹配结果存放在一个变量中以实现随后的对匹配结果的访问。
(2)当发现一个匹配则检查包含在[]中的字符串,查看是否为/bin/tcsh。如果不是则发送/bin/tcsh给chsh命令作为输入,如果是则仅仅发送一个回车符。这个简单的针对具体情况发出不同相响应的小例子说明了expect的强大功能。
(3)在一个正则表达时中,可以在()中包含若干个部分并通过expect_out数组访问它们。各个部分在表达式中从左到右进行编码,从1开始(0包含有整个匹配输出)。()可能会出现嵌套情况,这这种情况下编码从最内层到最外层来进行的。
4、使用超时
下一个expect例子中将阐述具有超时功能的提示符函数。这个脚本提示用户输入,如果在给定的时间内没有输入,则会超时并返回一个默认的响应。这个脚本接收三个参数:提示符字串,默认响应和超时时间(秒)。
#!/usr/bin/expect# Prompt function with timeout and default.
#脚本的第一部分首先是得到运行参数并将其保存到内部变量中set prompt [lindex $argv 0]set def [lindex $argv 1] set response $defset tout [lindex $argv 2]
send_tty "$prompt: "#send_tty命令用来实现在终端上显示提示符字串和一个冒号及空格set timeout $tout#set timeout命令设置后面所有的expect命令的等待响应的超时时间为$tout(-l参数用来关闭任何超时设置)。 expect " " {set raw $expect_out(buffer)
# remove final carriage returnset response [string trimright "$raw" " "]}if {"$response" == "} {set response $def}send "$response "
# Prompt function with timeout and default.set prompt [lindex $argv 0]set def [lindex $argv 1] set response $defset tout [lindex $argv 2]
(1)send_tty命令用来实现在终端上显示提示符字串和一个冒号及空格。
(2)set timeout命令设置后面所有的expect命令的等待响应的超时时间为$tout(-l参数用来关闭任何超时设置)。
(3)然后expect命令就等待输出中出现回车字符。如果在超时之前得到回车符,那么set命令就会将用户输入的内容赋值给变脸raw。随后的命令将用户输入内容最后的回车符号去除以后赋值给变量response。
(4)如果response中内容为空则将response值置为默认值(如果用户在超时以后没有输入或者用户仅仅输入了回车符)。最后send命令将response变量的值加上回车符发送给标准输出。
(1)该脚本没有使用spawn命令。
(2)该expect脚本会与任何调用该脚本的进程交互。
(3)如果该脚本名为prompt,那么它可以用在任何C风格的shell中。
% set a='prompt "Enter an answer" silence 10' Enter an answer: test
% echo Answer was "$a" Answer was test
prompt设定的超时为10秒。如果超时或者用户仅仅输入了回车符号,echo命令将输出
Answer was "silence"2
相关资讯 & & &
& (09月14日)
& (06月09日)
& (10月21日)
& (07月25日)
& (03月14日)
图片资讯 & & &
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款

我要回帖

更多关于 shell脚本expect 的文章

 

随机推荐