曾经见过别人用C实现了一个简单的Telnet客户端协议的程序,可以在这个程序加入自己的代码来捕获服务端的输出,根据这些输出来发送适当的指令来进行远程控制。
使用Perl一样可以实现这样的功能,然而,Expect做的更出色,而且除支持Unix/Linux平台外,它还支持Windows平台,它就是为系统管理和软件测试方面的自动交互类需求而产生的:Expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。
最好的学习方法就是边干边学,对于已经熟悉一种编程语言的人来说,用另一种新的语言来写程序解决问题,是很容易的事。所以大概了解一下基本语法后,就一边动手解决问题,一边查手册吧。
***************************************************************************************
【结合crontab定时执行任务,实现定时登陆服务器执行任务】
- #!/usr/bin/expect -f
- set timeout 30
- spawn ssh -l test 192.168.1.1
- expect "password:"
- send "mypassword\r"
- expect "~$*"
- send "/home/test/a.sh\r"
- send "exit\n"
- expect eof
- exit
expect 的核心功能,对于设定好的特定匹配形式,以相匹配的动作以应对。每一个expect后所跟的字符串(或者正则表达式)就是脚本所等待的匹配模式,每一个send 所做的工作就是对于各种的模式串,实施相应的动作。
第一行设定了脚本执行的程序,-f选项指的是expect执行一个文件
第二行,设定了本脚本所有的超时时间,单位是秒(s),如果超时,脚本将继续向下进行(比如在等待某个模式出现,超时以后,会进行下一语句)。
第三行,expect使用spawn命令来启动脚本和命令会话,这里启动的是ssh命令,这里的ssh命令将会以子进程的方式产生。
下面就是交互的过程:ssh -l 登陆以后,会给要求客户写入密码,所以等待出现“password:”,出现password:以后,需要写入密码,注意这里需要送去回车或者换行符,否则远端主机不会收到ssh请求的。登陆上系统之后,会出现命令提示符:~$,即系统已经登陆到了远端主机的shell,然后送去要执行的命令。完毕后推出远程机器(这个send "exit\r"前也可以有上一个命令的输出,也可以没有,因为上一个命令执行完毕后会顺序执行下一条)。
最后是等待标示子进程已结束的标示符eof,然后退出。(注:这个等待eof必须要有,如果没有eof,很可能在子进程没有结束前就退出,造成问题。)
语法说明:
Expect语言是基于Tcl的, 作为一种脚本语言,Tcl具有简单的语法:
cmd arg arg arg: 一条Tcl命令由空格分割的单词组成. 其中, 第一个单词是命令名称, 其余的是命令参数 ;
$foo: $符号代表变量的值. 在本例中, 变量名称是foo.;
[cmd arg] : 方括号执行了一个嵌套命令. 例如, 如果你想传递一个命令的结果作为另外一个命令的参数, 那么你使用这个符号;
"some stuff" :双引号把词组标记为命令的一个参数. "$"符号和方括号在双引号内仍被解释;
{some stuff}:大括号也把词组标记为命令的一个参数. 但是, 其他符号在大括号内不被解释;
反斜线符号是用来引用特殊符号. 例如:n 代表换行. 反斜线符号也被用来关闭"$"符号 , 引号,方括号和大括号的特殊含义 。
set myname [lindex $argv 0] 这句获取外部传入的第一个参数(argv 0)并传给变量myname,如果获取多个参数则使用argv 1,argv 2,以此类推。
另外,expect支持一般语言所常用的if,for等流程控制语句,这个可以参看expect高级介绍
【其他】
[expect "password:"]
这里的expect也是expect的一个内部命令,有点晕吧,expect的shell命令和内部命令是一样的,但不是一个功能,习惯就好了。这个命令的意思是判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间后返回,这里等待时长就是前面设置的30秒
[send "ispass\r"]
这里就是执行交互动作,与手工输入密码的动作等效。
温馨提示: 命令字符串结尾别忘记加上 “\r”,如果出现异常等待的状态可以核查一下。
[interact]
执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行一段命令就退出,可改为[expect eof]。
$argv数组存储从脚本中传递进来的变量,数组索引从0开始。
expect -re "\[(.*)]:" //re指示为正则表达式
set prompt [lindex $argv 0] //将参数0存储到prompt变量中。
8、示例1
if {$argc<2}
{
send_user "usage: $argv0 file user1 user2 ... "
exit
}
send_user命令用来显示使用帮助信息到父进程(一般为用户的shell)的标准输出。也可以用puts。
函数lindex从列表/数组得到一个特定的元素。[]用来实现将函数lindex的返回值作为set/send命令的参数。
exp_continue同C中的continue;eof(end-of-file)关键字用于匹配结束符,比如文件的结束符、FTP传输停止等情况,在这个关键字后跟上动作来做进一步的控制,断开连接,退出等。