最近因为学习tcl脚本语言,需要用到expect扩展包,结合网上的资料和自己的学习情况做一下整理。
expect是一种基于TCL的解释型脚本语言,能够实现自动和交互式任务进行通信,而无需人的干预。expect由一系列expect-send对组成:expect等待输出中输出特定的字符,然后发送特定的响应。
Expect需要Tcl编程语言的支持,要在系统上运行Expect必须首先安装Tcl。expect相关软件包版本有多个,如expect-tcl8.3、expectk、expect-dev等,可根据自身需求选择安装。
我们工作中主要利用expect扩展包来实现telnet到远程终端、输入命令来控制终端的运行,在执行一些重复性工作或是要输入大量配置时相当方便。
一、expect的一个小例子(windows环境下)
package require Expect
spawn telnet 192.168.86.172
set telnet $spawn_id
expect "enable passward: "
send "admin\r"
expect "#"
send "config terminal\r"
解释:这是一个.tcl文件中的一段脚本,首行package require Expect声明加载expect包,然后就可以调用expect相关命令了。
"spawn"用来启动脚本和命令的会话,常用的命令有telnet、ssh、rlogin等,这里启动的是telnet命令,实际上命令是以衍生子进程的方式来运行的。另外值得说明的是,只有spawn的内容才能被expect捕获到。
"expect"对执行command后的输出进行匹配检测,可使用正则表达式和通配符等。
"send"用于发送command。
二、expect的语法以及注意点
脚本都有相似之处,Expect是基于TCL的,有以下几个地方需要注意:
1)“{”与前面的字符必须空一格,否则会被解释器认为是与前面字符是同一个词。
2)一条命令结尾的“;”是可有可无的,但如果同一行写来多条命令,则必须以“;”分隔,否则会被误认为只有一条命令。
3)expect检测的双引号中的字符串,若有多行,除前引号后面的那行外,其余的均需要顶格写,否则空格也会被匹配进去,或者前面用通配符“*”来匹配也行。
4)如果发现脚本挂在了某个点上,可以试着在前一个send前面增加一小会儿sleep时间。因为在收到提示后,一系列的程序(rn, ksh,zsh,telnet,etc.)和设备抛弃或忽略的按键等响应的“太快”了。
5)某些程序每次产生的结果都是不一样的,此时最好用通配符来匹配。
三、流程控制
expect除了基于TCL的if/else等条件判断外,其自身也提供来expect流程控制的功能,示例如下:
send "sudo -s"
expect { "password: " {send "vmkid\r"; exp_continue}
"~# " {puts "------break----"}
}
意思是发送“sudo -s”命令后,有两种可能的输出,如果输出为“password:“,则执行后面所跟的”{}“中的命令,发送密码并跳出此次循环,直到输出结果为”# “才跳出整个expect循环。另外,expect与if/else等也可循环嵌套使用。
四、过程
某些代码有时是需要重复操作的,比如设备在某些特定的场合下可能需要反复重启等,此时我们可以将其写在某一个过程中,直接调用该过程,以减少和简化代码。
proc SystemReset {x} {
if {$x == 1} {
spawn adb shell
expect "~# "
send "reboot\r"
expect "*"
} else {
}
}
如上,在需求重启手机时,我们只要调用”SystemReset 1"便可以了。
五、输出日志
log_file [文件名]
这个命令让expect在你设置的文件中记录输出信息。必须注意,这个选项并不影响控制台输出信息,不过如果你通过crond设置expect脚本在半夜运行的话,你就确实可能需要这个命令来记录各种信息了。例如:log_file expect.log
log_user 0/1
这个选项设置是否显示输出信息,设置为1时是缺省值,为0 的话,expect将不产生任何输出信息,或者说简单地过滤掉控制台输出。必须记住,如果你用log_user 0关闭了控制台输出,那么你同时也就关闭了对记录文件的输出。
六、超时
expect的timeout时间, 是以秒为单位, 如果设置为0, 是根本就不等待, 设置为-1, 是永远等待.
set timeout 30
附录:expect的程序介绍
1,主要程序源文件概述(在编译过程中提取)
exp_command.c --->
expect.c --->
exp_inter.c --->
exp_regexp.c --->
exp_tty.c --->
exp_log.c --->
exp_main_sub.c --->expect 的主程序(expect/expectk共用)
exp_pty.c --->
exp_trap.c --->
exp_strf.c --->
exp_console.c --->
exp_glob.c --->
exp_win.c --->
exp_clib.c --->
exp_closetcl.c --->
exp_memmove.c --->
exp_tty_comm.c --->
exp_chan.c --->
Dbg.c --->
pty_termios.c --->
exp_select.c --->
exp_event.c --->
exp_main_exp.c --->expect 主程序(壳)
exp_main_tk.o --->expectk主程序(壳)
2,需要关照的Tcl命令
exp_close --->关闭exp_spawn打开的链接 --->Exp_command.c exp_init_most_cmds
exp_continue --->重新执行expect --->未在expect源代码中定义
exp_disconnect --->与儿子进程断开关联 --->Exp_command.c
exp_exit --->重写了Tcl默认的exit命令 --->Exp_command.c
exp_fork --->克隆 当前的expect进程 --->Exp_command.c
exp_getpid --->获取expect进程id --->Exp_command.c
exp_inter_return ---> --->Exp_command.c
exp_interact ---> --->Exp_inter.c
exp_internal ---> --->Exp_command.c
exp_interpreter ---> --->Exp_command.c
exp_log_file --->设置记录会话路径文件 --->Exp_command.c
exp_log_user --->关闭和打开交互过程的标准输出 --->Exp_command.c
exp_match_max --->设置交互buff的大小,默认2000 --->Expect.c
exp_open ---> --->Exp_command.c
exp_overlay ---> --->Exp_command.c
exp_parity ---> --->Expect.c
exp_pid ---> --->Exp_command.c
exp_remove_nulls ---> --->Expect.c
exp_send --->expect发送命令 --->Exp_command.c
exp_send_error --->发送错误输出 --->Exp_command.c
exp_send_log --->发送到log文件 --->Exp_command.c
exp_send_tty ---> --->Exp_command.c
exp_send_user ---> --->Exp_command.c
exp_sleep --->expect等待(秒) --->Exp_command.c
exp_spawn --->启动外部进程 --->Exp_command.c
exp_strace ---> --->Exp_command.c
exp_stty ---> --->Exp_tty.c
exp_system ---> --->Exp_tty.c
exp_timestamp ---> --->Expect.c
exp_trap ---> --->Exp_trap.c
exp_version ---> --->Exp_main_sub.c
exp_wait --->等待exp_spawn启动的进程结束退出 --->Exp_command.c
expect --->expect主要命令 --->Expect.c
expect_after ---> --->Expect.c
expect_background ---> --->Expect.c
expect_before ---> --->Expect.c
expect_tty ---> --->Expect.c
expect_user ---> --->Expect.c