样式匹配
*号
expect “hi*”
send “$expect_out(0,string) $expect_out(buffer)”
输入philosophic,输出为hilosophic philosophic,hi*匹配的是hilosophic
如果是hi*hi,则匹配的是hilosophi
如果是*hi*,则匹配的是philosop hi c\n ,而不是p hi losophic\n,因为匹配是从左到右进行,且*尽可能匹配更多的字符,但是要符合样式,所以第一个*匹配的字符多一些
以*开头的样式并不经常用到,像*hi*,它能把前面不匹配的数据保存在expect_out(0,string),但是expect_out(buffer)中也会保存,所以这点也没什么意义
以* 结尾的样式经常需要认真考虑,因为程序的输出不像人的标准输入一样是一行一行而是一堆一堆进行,这样可能匹配的最多数据还没发送完,程序已经返回了。因为* 可以匹配任何东西,包括空字符串,可以用它做结尾匹配人的一些输入,并清理输入缓存。
例子:
expect “220*” ;#在ftp的例子中会出错,可能程序没准备好,即已经返回了
expect “220*ready” ;#可以在整个的输入完成后才返回
expect “220*re” ;#可以作为一个简写,但是”220*r”就太简单了,它的r就可能匹配的server
更多Glob样式
?匹配一个字符,a?b匹配abd但不匹配abcd
[]匹配它范围内的任意字符,[abcdef0123456789]能匹配任意十六进制数,也可以写成[a-f0-9].如果要匹配短横杠,要将其写在最前或者最后,例如[-a-c]匹配-,a,b,c
因为中括号也是特殊的expect的字符,它里面的式子要立刻求值,所以[]样式必须写成下面两种方式:
expect “\[a-f0-9]” ;#推荐使用
expect {[a-f0-9]} ;#会出现预想不到的情况,因为里面所有的值都不会进行运算
也可以括号的第二部分加\,但这不是必须的
斜杠
太复杂,举例:
expect “\n” ;#matches \n
expect “\r” ;#matches \r
expect “\z” ;#matches z
expect “\{“ ;#matches {
expect “*” ;#matches * and ? and X and abc
expect “\\*” ;#matches * but not ? or X or abc
expect “?” :#matches * and ? and X but not abc
expect “\\?” ;#matches ?but not * or X or abc
expect “n” ;#matches n
expect “\n” ;#matches \n
expect “\\n” ;#matches n
expect “\\\n” ;#matches \n
expect “\\\\n” ;#matches sequence of \ and n
expect “\\\\\n” ;#matches sequence of \ and \n
expect “\\\\\\n” ;#matches sequence of \ and n
expect \\\\\\\n ;#matches sequence of \ and \n
expect \\\\\\\n ;#matches sequence of \,and \, and n
expect “*” ;#matches anything
expect “\*” ;#matches anything
expect “\\*” ;#matches *
expect “\\\*” ;#matches *
expect “\\\\*” ;#matches \ followed by anything
expect “\\\\\*” ;#matches \ followed by anything
expect “\\\\\\*” ;#matches \ followed by *
expect “\\\[“ ;#matches literal [
a procedure named xy returns the string “n*w”
expect “[xy]” ;#matches n followed by anything
expect “\[xy]” ;#matches x or y
expect “\\[xy]” ;#matches n followed by anything followed by w
expect “\\\[xy]” ;#matches [xy]
expect “\\\\[xy]” ;#matches \ followed by n followed…
expect “\\\\\[xy]” ;#matches sequence of \ and x or y
注意匹配的过程,tcl按照顺序匹配,字符的转义等,都发生在这个阶段,而pattern matcher在第二阶段不会有转义了,只是按照字符的实际匹配
expect “\\\ n” 在经tcl处理后字符为\,’\n’,所以第二阶段匹配的是’\n’,就是第一阶段的结果来
expect “\\ n” 在经tcl处理后字符为\ ,’n’ ,所以第二阶段匹配的是’n’
expect “\\\*” 在经过tcl处理后字符为’\’ ‘*’, 因为还有一个\存在,所以第二阶段匹配是严格的*,而不是anything
处理超时
expect “hi”
expect “hi” {}
expect {
“hi” {send “You said hi\n”}
“hello” {send “Hello yourself\n”}
“bye”
}
只有expect命令中最后一个动作可以省略
增加错误检查
spawn ftp $argv
set timeout 10
expect {
“connection refused” exit
“unknown host” exit
“Name”
}
send “anonymous\r”
区分超时和连接成功
expect {
timeout {puts “timed out”,exit }
“connection refused” exit
“unknown host” exit
“Name”
}
使用超时取代一切错误处理
expect {
timeout exit
“Name”
}
事实上,用来超时代替一切,会增加程序的反应时间,但要处理每种错误,就要在脚本上花费很多时间。通用用超时处理一些模糊的条件,而用一个样式处理大多数的错误,例如ftp的错误都是以4或者5开头,例子:
expect {
timeout {unexpected…}
“^\[45]” {error…}
“ftp>”
}
在一些超时比较长的程序,可以先用ping判断主机是否存在
spawn ping $host
set timeout 2
expect “alive” {exit 0} timeout {exit 1}
可以用echo $?察看脚本的返回值,可以为timeout增加一些内容输出,因为ping有返回的话自会打印结果,如果超时,脚本会在ping返回结果以前将其终止并结束,所以脚本要增加输出内容
spawn ping $host
set timeout 2
expect “alive” {exit 0} timeout{
puts “no answer from $host”
exit 1
}
自我设定超时时间的脚本maxtime,用法%maxtime 20 prog
#!/usr/bin/expect –
set timeout [lindex $argv 0]
spawn [lindex $argv 1]
expect
文件结尾处理
在以上maxtime脚本中,如果程序先结束,则脚本随之结束,因为没有要匹配的样式,所以程序如果在超时以前没有结束,脚本也会返回。
#!/usr/bin/expect –
set timeout [lindex $argv 0]
eval spawn [lindex $argv 1 end]
expect {
timeout {puts “took toomuch time”}
eof {puts “finished in time”}
}
%maxtime 2 sleep 5 和%maxtime 5 sleep 2返回的结果是不一样的,eof和timeout一样,也是一个特殊的匹配样式,表示程序结束
spawn的注意:
用eval spawn [lrange $argv 1 end] 来执行spawn命令,因为spawn会将它后面lrange取得的所有参数当作一个命令。
eof的注意
脚本:
spawn ping $host
set timeout 2
expect “alive” {exit 0} timeout {exit 1}
如果ping发现主机不存在,返回eof,则expect正常结束,它返回0,正常情况下脚本执行成功才返回0,但是这种情况下主机不存在也会返回0,所以要对eof处理,修订脚本如下:
spawn ping $host
set timeout 2
expect “alive” {exit 0} timeout {exit 1} eof {exit 1}
如果对超时和异常结束作相同的处理,可以default样式,它可以匹配所有可能的情况:
expect “alive” {exit 0} default {exit 1}
close命令
eof不仅可以从process送往expect,也可以从expect送往process,使process退出,在expect脚本中用close显式结束一个进程,通常情况下,process和expect一个结束了,另一个也会随之结束
忽略eof的程序
一类是运行在Raw模式下的程序,像telnet,这种模式下,不对输入的字符作特殊的翻译,^c,^d都不起特殊的作用,只是普通字符,eof也是,所以没有正常退出的话要手动杀死进程,用kill命令
第二类是看到eof就忽略已经接收到的字符马上退出的进程,像ftp,vi,下面的脚本:
spawn ftp…
#assume username and password are accepted here
expect “ftp> “ {send “get file1 \r”}
expect “ftp>” {send “get file2 \r”}
expect “ftp>” {send “get file3\r”}
上面的脚本只能接收到两个文件,因为在收到第三个文件的请求时也收到eof,因为脚本执行完毕,0所以退出,解决办法是在行末加一句epect命令,使脚本不能提前结束
spawn vi file
send “ifoo\033:wq\r”
错误,文件没有内容,正确的如下:
spawn vi file
send “ifoo\033:wq\r”
expect
wait命令
wait 等待一个进程死亡,-noflag用来避免时延
13.expect重要的命令(拿来)
send命令
send “hello world”
send “hello world\n”
expect speak ;#speak为一脚本
expect命令
expect “hi\n”
send “hello there!\n”
expect_out(0,string)用来保存匹配的字符,匹配字符和它以前的字符保存在expect_out(buffer)
expect “hi\n”
send “you typed <$expect_out(buffer)>”
send “but I only expected <$expect_out(0,string)>”
定位
^string只匹配输入的开始部分
像^hi匹配hiccup但是不匹配sushi
string$只匹配输入的结束部分
像hi$匹配sushi但是不匹配hiccup
而^hi$只匹配hi
expect的输入以自己接受到的字符为准,并不考虑行界,如果没有定位符,它会匹配最早遇到的合适字符
不匹配的情况
例子说明:
expect “hi”
send “$expect(0,string) $expect_out(buffer)”
此时输入philosophic,输出为hi phi
如果再次执行这两句命令,输出为hi losophi
input buffer用来保存expect 取得的输入字符串,第一匹配结束后,expect_out(0,string)为hi,expect_out(buffer)为phi,下一次的匹配从 losophi开始,所以上面的两个值分别为hi,losophi,此时input buffer里为c\n,因为不能够匹配hi,所以如果进行第三次匹配,会超时,而上面的expect_out(0,string) expect_out(buffer)与第二次相同,所以第三次超时后会输出hi losophi
超时值可以设置
set timeout INTERGER
INTERGER为一整数值,如果为-1表示永久等待,如果为0表示立即返回
多种样式匹配
expect “hi” { send “You said hi\n” } \
“hello” { send “Hello yourself\n” } \
“bye” { send “That was unexpected\n” }
命令会对hi,hello,bye同时进行匹配
注意书写格式,expect “hi” send “You said hi\n” ;#错误,You said hi会被解释成一个命令
可用的其它格式
expect {
“hi” { send “You said hi\n”}
“hello” { send “Hello yourself\n”}
“bye” { send “That was unexpected\n”}
}
expect “hi” {
send “You said hi\n”
} “hello” {
send “Hello yourself\n”
} “bye” {
send “That was unexpected\n”
}
expect {
“hi” {
send “You said hi\n”
}
“hello” {
send “Hello yourself\n”
}
“bye” {
send “That was unexpected\n”
}
}
expect “exit” {exit 1} “quit” abort 为写在一行的格式,匹配命令带参数的话一定要用大括号括起来
例子 Timed Reads
#!/usr/bin/expect –
set timeout $argv
expect “\n” {
send [string trimright “$expect_out(buffer)” “\n”]
}
%timed-read 60 ;#作用是等待60,接收此段时间内用户的输入
可以用于机器重起的脚本
echo “Rebooting …”
echo “Want to poke around before coming up all the way?”
answer=’timed-read 60’
可以在无人值守的情况下让机器重起
spawn命令
spawn命令用于启动一个进程
例子ftp-rfc
#!/usr/local/bin/expect –
#retrieve an RFC (or the index) from uunet via anon ftp
if { [llength $argv]== 0} {
puts “usage: ftp-rfc {-index|#}”
exit 1
}
set timeout -1
spawn ftp ftp.uu.net
expect “Name”
send “anonymous\r”
expect “Password:”
send don@libes.com\r
expect “ftp> “
send “cd inet/rfc\r”
expect “ftp> “
send “binary\r”
expect “ftp> “
send “get rfc$argv.z\r”
expect “ftp> “
exec uncompress rfc$argv.z
例子中binary的命令在于让ftp传文件时不要作任何转换,注意结尾是\r,不是\n,视为手动输入和机器输入的差别?? 最后一句的作用是解压缩。
interact命令
interact命令用于脚本执行完简单的命令后人手动介入,只在所属的spawn进程空间有效
例子aftp
#!/usr/bin/expect –
spawn ftp $argv
expect “Name”
send “anonymous\r”
expect “Password:”
send don@libes.com\r
interact
可以用send “$env(USER)@libes.com\r”
proc domainname {} {
set file [open /etc/resolv.conf r]
while { [gets $file buf] !=-1} {
if {[scan $buf “domain %s” name]==1} {
close $file
return $name
}
}
close $file
error “no domain declaration in /etc/resolv.conf”
}
send “$env(USER)@[domainname]\r”
3.与shell结合例
另例