1. 流程
-
运行一个Expect程序
-
spawn寄生目标程序,接管目标程序的输入输出
-
expect判断目标程序的输出,做相应逻辑判断
-
send用来向目标程序输入
-
interact将控制权交给用户
2. spawn
寄生目标程序,接管目标程序的输入输出,这通常都是expect脚本的第一步,默认情况下,spawn会回显命令名称和参数,可以用-noecho参数来让它不回显
3. send
Characters are sent immediately although programs with line-buffered input will not read the characters until a return character is sent. A return character is denoted "\r".
向目标程序输入,字符直接发送给程序,但要注意,行缓冲输入的程序直到收到一个return字符才会接收这些输入字符,return字符用"\r"表示
-
send_user 输入到 stdout,一般做提示用
4. expect
匹配被寄生的程序的输出与模式,如果匹配上,执行相应动作。匹配成功或超时或遇到文件尾时都会返回
一旦匹配上,则开始执行对应的语句。默认情况下,模式采用Tcl语言的string match命令匹配
模式中可用的特殊符号:
符号 |
含义 |
---|---|
* |
匹配任何字符串,包括空null字符串 |
? |
匹配任意单个字符 |
[chars] |
匹配chars中指定的字符集合,比如[A-Za-z]匹配所有字母,再比如[abc]匹配abc三个字符中的任意 |
\x |
匹配字符x,提供了一种防止特殊字符转义的匹配方法,比如\\可以匹配反斜杠这个字符,\?可以匹配问号,\*可以匹配* |
可以加上-re参数指定使用正则表达式匹配
4.1. exp_continue
重新调用expect
4.2. expect_user
跟expect一样,不过它从标准输入读数据,默认情况下,读取工作在cooked模式下,所以,必须敲回车才能让expect看到数据
4.3. expect_before & expect_after
比如有下面的例子:
expect {
"login:" {send "$user\r"}
eof eofproc
}
expect {
"password:" {send "$password\r"}
eof eofproc
}
expect {
"$prompt" {send "$command\r"}
eof eofproc
}
可以用expect_after
expect_after eof eofproc
expect "login:" {send "$user\r"}
expect "password:" {send "$password\r"}
expect "$prompt" {send "$command\r"}
最近的expect_after都会加到expect里组成一个新的执行体,expect_after和expect_before的区别在于顺序不同,当expect和expect_after或expect_before匹配同一个模式时,expect_after会后于expect执行,而expect_before会先于expect执行
4.4. expect_out
expect_out(0, string)
expect匹配的字符串
5. interact
6. 自动生成Expect脚本
7. cooked模式和raw模式
cooked模式下会对特殊字符做预处理,如退格、删除、Control-C和Control-D等。raw模式不会做预处理,数据被原样传递给应用程序。
比如,在cooked模式下,输入ABC<Backspace>D,最终传给程序的是ABD。而在raw模式下,程序收到的是ABC加一个退格符再加一个D
8. Tcl
Tcl是一种工具命令语言,可参考资料:http://tmml.sourceforge.net/doc/tcl/
常用语法
-
方括号[],相当于sh编程中的`符号,被括起来的语句会先执行并返回结果
-
proc 子程序(函数)
-
set 设置变量
-
info exists varName 变量是否存在
-
lindex 取数组元素
-
string equals 比较string是否相等
9. case
#!/usr/bin/expect -f
if { $argc >= 1 } {
set target_host [lindex $argv 0]
}
if { $argc >= 2 } {
set target_dir [lindex $argv 1]
} else {
set target_dir ""
}
set timeout -1
set bridge_host "jumper.abc.com"
set username "xiaomi"
set password "1234567"
proc go_target { target_host target_dir } {
if { [info exists target_host] } {
send "ssh ${target_host}\r"
expect {
"$*" { send "sudo -uabc -i\r" }
}
if { ![string equal ${target_dir} ""] } {
expect {
"*abc" {
send "cd ${target_dir}\r"
}
}
}
interact
}
}
spawn ssh "${username}@${bridge_host}"
expect {
"Jumper*" { go_target $target_host $target_dir }
"*Password*" {
send_user "please enter verification code (6 digit):"
expect_user -re "(.*)\n"
set veri_code $expect_out(0,string)
send "${password}${veri_code}\n"
expect {
"xiaomi@Jumper" { go_target $target_host $target_dir }
}
}
}
10. 参考资料
https://www.safaribooksonline.com/library/view/exploring-expect/9781565920903/ch11s10.html#
http://www.tcl.tk/man/expect5.31/expect.1.html