Linux下⾃动应答⼯具Expect使⽤指南
关注 「奇妙的 Linux 世界」
设为「星标」,每天带你玩转 Linux !
Expect 是⽤于⾃动化交互式应⽤程序
1. 软件介绍
现代的 Shell 对程序提供了最⼩限度的控制(程序的开始/停⽌/关闭等),⽽把交互的特性留给了⽤户。这意味着有些程序,你不能⾮交互的运⾏,⽐如说 passwd 命令。有⼀些程序可以⾮交互的运⾏,但在很⼤程
度上丧失了灵活性,⽐如说 fsck 命令。这表明 Unix 的⼯具构造逻辑开始出现问题。Expect 恰恰填补了其中的⼀些裂痕,解决了在 Unix 环境中长期存在着的⼀些问题。
Expect 使⽤ Tcl 作为语⾔核⼼,不管程序是交互和还是⾮交互的,Expect 都能运⽤。Tcl 实际上是⼀个⼦程序库,这些⼦程序库可以嵌⼊到程序⾥从⽽提供语⾔服务。最终的语⾔有点象⼀个典型的 Shell 语⾔。⾥⾯有给变量赋值的 set 命令,控制程序执⾏的 if, for, continue 等命令,还能进⾏普通的数学和字符串操作。
Expect 是在 Tcl 基础上创建起来的并且还提供了⼀些 Tcl 所没有的命令:
spawn命令激活⼀个 Unix 程序来进⾏交互式的运⾏
send命令向进程发送字符串
expect命令等待进程的某些字符串且⽀持正规表达式并能同时等待多个字符串
# 命令格式
expect patlist1 action1
该命令⼀直等到当前进程的输出和以上的某⼀个模式相匹配,或者等到时间超过⼀个特定的时间长度,或者等到遇到了⽂件的结束为⽌。每⼀个 patlist 都由⼀个模式或者模式的表(lists)组成。如果有⼀个模式匹配成功,相应的 action 就被执⾏,执⾏的结果从 expect 返回。
被精确匹配的字符串(或者当超时发⽣时,已经读取但未进⾏匹配的字符串)被存贮在变量 expect_match ⾥⾯。如果 patlist 是 eof 或者timeout 的情况,则发⽣⽂件结束或者超时时才执⾏相应的 action 动作。⼀般超时的默认值是 10 秒,但可以⽤类似 "set timeout 30" 之类的命令把超时时值设定为 30 秒。
# 下⾯的⼀个程序段是从⼀个有关登录的脚本⾥⾯摘取的
# abort是在脚本的别处定义的过程,⽽其他的action使⽤类似与C语⾔的Tcl原语
expect "*welcome*" break
"*busy*" {print busy;continue}
"*failed*" abort
timeout abort
模式是通常的 C Shell 风格的正规表达式,模式必须匹配当前进程的从上⼀个 expect 或者 interact 开始的所有输出(所以统配符*****使⽤的⾮常的普遍)。但是,⼀旦输出超过 2000 个字节,前⾯的字符就会被忘记,这可以通过设定 match_max 的值来改变。
字符可以使⽤反斜杠来单独的引⽤,反斜杠也被⽤于对语句的延续,如果不加反斜杠的话,语句到⼀⾏的结尾处就结束了。这和 Tcl 也是⼀致的。Tcl 在发现有开的单引号或者开的双引号时都会继续扫描。⽽且,分号可以⽤于在⼀⾏中分割多个语句。这乍听起来有点让⼈困惑,但是,这是解释性语⾔的风格,但是,这确实是 Tcl 的不太漂亮的部分。
spawn passwd [lindex $argv 1]
expect eof {exit 1}
timeout {exit 2}
"*No such user.*" {exit 3}
"*New password:"
send "[index $argv 2]\r"
expect eof {exit 4}
timeout {exit 2}
"*Password too long*" {exit 5}
"*Password too short*" {exit 5}
"*Retype new password:"
send "[index $argv 3] "
expect timeout {exit 2}
"*Mismatch*" {exit 6}
"*Password unchanged*" {exit 7}
" "
expect timeout {exit 2}
"*" {exit 6}
eof
这个脚本退出时⽤⼀个数字来表⽰所发⽣的情况。0 表⽰ passwd 程序正常运⾏,1 表⽰⾮预期的死亡,2 表⽰锁定,等等。使⽤数字是为了简单起见。expect 返回字符串和返回数字是⼀样简单的,即使是派⽣程序⾃⾝产⽣的消息也是⼀样的。实际上,典型的做法是把整个交互的过程存到⼀个⽂件⾥⾯,只有当程序的运⾏和预期⼀样的时候才把这个⽂件删除。否则这个 log 被留待以后进⼀步的检查。
这个 passwd 检查脚本被设计成由别的脚本来驱动。这第⼆个脚本从⼀个⽂件⾥⾯读取参数和预期的结果。对于每⼀个输⼊参数集,它调⽤第⼀个脚本并且把结果和预期的结果相⽐较。(因为这个任务是⾮交互的,⼀个普通的⽼式 shell 就可以⽤来解释第⼆个脚本)。⽐如说,⼀个 passwd 的数据⽂件很有可能就象下⾯⼀样。
第⼀个域的名字是要被运⾏的回归脚本。第⼆个域是需要和结果相匹配的退出值。第三个域就是⽤户名。第四个域和第五个域就是提⽰时应该输⼊的密码。减号仅 仅表⽰那⾥有⼀个域,这个域其实绝对不会⽤到。在第⼀个⾏中,bogus 表⽰⽤户名是⾮法的,因此 passwd 会响应说:没有此⽤户。expect 在退出时会返回 3,3 恰好就是第⼆个域。在最后⼀⾏中,^C 就是被切实的送给程序来验证程序是否恰当的退出。
2. ⼯具安装
源代码和下载地址都是由 Linux 软件基⾦会维护的(sourceforge)
# 因为Expect需要Tcl编程语⾔的⽀持
$ sudo yum install -y gcc
$ sudo yum install -y tcl tclx tcl-devel
# centos
$ sudo yum install expect
# 因为Expect需要Tcl编程语⾔的⽀持ssh工具手机版
$ sudo apt install -y gcc
$ sudo apt install tcl
# ubuntu
$ sudo apt install expect
# 下载源代码包
# 官⽹主页地址: sourceforge/projects/expect/
wget "sourceforge/projects/expect/files/Expect/5.45.4/expect5.45./download"
# 源代码编译
$ sudo ./configure \
-
-with-tcl=/usr/lib \
--with-tclinclude=/usr/include/tcl-private/generic
# 源代码安装
$ sudo make && make install
3. 基础知识
主要介绍常见的 4 个命令的使⽤⽅式
我们知道,send 命令⽤于发送信息到进程中,expect 命令则是根据进程反馈的信息进⾏对应逻辑的交互的。⽽ spawn 命令后的 send 和 expect 命令其实都是和使⽤ spawn 命令打开的进程进⾏交互的。
需要说明的是 interact 命令其实⽤的不多,⼀般情况下使⽤ spawn、send 和 expect 命令就可以很好的完成任务了。但在⼀些特殊场合下,使⽤ interact 命令还是能够发挥很好作⽤的。interact 命令主要⽤于退出⾃动化进⼊⼈⼯交互。⽐如我们使⽤ spawn、send 和expect 命令完成了 ftp 登陆主机,执⾏下载⽂件任务,但是我们希望在⽂件下载结束以后,仍然可以停留在 ftp 命令⾏状态,以便⼿动的执⾏后续命令,此时使⽤ interact 命令就可以很好的完成这个任务。
编号命令作⽤
1send send 命令接收⼀个字符串并将该参数发送到进程中
2expect expect 通常⽤来等待进程的反馈再发送对应的交互命令
3spawn spawn 命令⽤来启动新的进程
4interact允许退出⾃动化进⼊⼈⼯交互
4. 控制结构
介绍 TCL 语⾔的控制结构
[1] if else
set alias_host [lindex $argv 0]
set b1_password ASJZOMxlgM^9
set b2_password a0yDuePSLUGM
if {$argc!=1} {
echo "请输⼊想要远程连接的服务器: [b1|b2]"
exit 1
}
if {$alias_host=="b1"} {
spawn ssh escape@192.168.100.100 -p 22
expect "*password*" {send "$b1_password\r"} interact
} elseif {$alias_host=="b2"} {
spawn ssh escape@192.168.100.101 -p 22
expect "*password*" {send "$b2_password\r"} interact
} else {
send "请输⼊想要远程连接的服务器: [b1|b2]"
}
[2] switch
#!/usr/bin/expect
set timeout 10
set alias_host [lindex $argv 0]
set b1_password ASJZOMxlgM^9
set b2_password a0yDuePSLUGM
switch -glob -- $file1 {
b1 {
spawn ssh escape@192.168.100.100 -p 22 expect "*password*" {send "$b1_password\r"} interact
}
b2 {
spawn ssh escape@192.168.100.101 -p 22 expect "*password*" {send "$b2_password\r"} interact
}
[3] while
#!/usr/bin/expect
set test 0
while {$test<10} {
set test [expr {$test + 1}]
if {$test > 7}
break
if "$test < 3"
continue
}
[4] catch
error "This is a error for test"
}
catch Error test
puts $test
5. 简单使⽤
下⾯是⼀些简单的⽰例代码,主要帮助我们理解 expect 的使⽤。
[1] 系统指定修改⽤户密码
[escape@linuxworld ~]$ passwd
Changing password for user escape.
Changing password for escape.
(current) UNIX password:
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
# p
#!/usr/bin/expect -d
set timeout 30
spawn passwd [lindex $argv 1]
set password [lindex $argv 2]
expect "*New password:*" {send "$password\r"}
expect "*Retype new password:*" {send "$password\r"}
expect eof
[2] 登陆远程服务器并停留在远程服务器上
# p
#!/usr/bin/expect
set timeout 30 # 设置超时时间
set host "100.200.200.200"
set username "root"
set password "123456"
# 给ssh运⾏进程加个壳⽤来传递交互指令
spawn ssh $username@$host
# 判断上次输出结果⾥是否包含指定的字符串
expect {
# exp_continue表⽰继续执⾏下⼀步
"*yes/no" {send "yes\r";exp_continue}
# 匹配即可发送密码到对应进程中
"*password*" {send "$password\r"}
}
# 执⾏完成后保持交互状态
interact
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论