- 论坛徽章:
- 0
|
最近调试程序,由于测试数据集过大,程序要跑几天几夜, 还好,测试集有一定的独立性,这就使我有空子钻了---把程序分到多台机子上运行,再把数据重定向到本机上,嘿嘿。
这样搞过一次,用了8台机子,运行了10个多小时,还是偷偷的ssh到别人的机子上运行的---不要说我rp不好,反正晚上嘛,我偷偷的用一下也没什么影响:)
不过,其实发现这样其实还是很麻烦的, 要分别ssh 上去,还要手工计算分配数据段,重定向。。。etc。。总之用的机子越多越麻烦,还容易出错。。
于是乎,自然想到写段 shell script 解决,不过很遗憾的发现,shell的话 ssh上去就停在那里的,其实也不奇怪,shell 为了安全起见,对程序提供了最小限度的控制(想启动的程序发送消息等),而把所有的交互都留给了用户, 也就是说shell基本不能运行交互式的程序了---比如那些执行期间要你输入选择的程序。。
我这样的需求,那用shell跟不用差不多了----到交互的时候都是要自己手动敲命令的。。
当然不甘心了, google之,发现有个专门干这个事的东西: expect ---以前从来没听过这个 :)
“Expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预”, 粗略看了下语法,感觉我的问题可以很轻易的解决,试了一下,果不其然^_^, expect真是个好东西。。
贴下我的expect script :
1 #! /usr/bin/expect
2 proc process { name disk step } {
3 spawn ssh $name
4 expect "*:)*"
5 puts "ssh $name ok"
6 send " cd **some dir** \n "
7 expect "*:)*"
8 puts "cd ok"
9 append file "data_" $disk "_" $step
10 puts $file
11 append param1 "--disk=" $disk
12 append param2 "--step=" $step
13 send " ./moon $param1 $param2 > $file & \n"
14 expect "*:)*"
15 puts " run moon ok"
16 send " exit \n"
17 expect "*:)*"
18 puts "exit ok"
19 }
20
21 set step [ expr 140000/$argc +1 ]
22 set disk 0
23 for { set i 0 } { $i Expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。
Expect的作者Don Libes在1990年开始编写Expect时对Expect做有如下定义:
Expect是一个用来实现自动交互功能的软件套件(Expect [is a] software suite for automating interactive tools)。
引用: Expect语言是基于Tcl的, 作为一种脚本语言,Tcl具有简单的语法:
cmd arg arg arg
一条Tcl命令由空格分割的单词组成. 其中, 第一个单词是命令名称, 其余的是命令参数 .
$foo
$符号代表变量的值. 在本例中, 变量名称是foo.
[cmd arg]
方括号执行了一个嵌套命令. 例如, 如果你想传递一个命令的结果作为另外一个命令的参数, 那么你使用这个符号 .
"some stuff"
双引号把词组标记为命令的一个参数. "$"符号和方括号在双引号内仍被解释 .
{some stuff}
大括号也把词组标记为命令的一个参数. 但是, 其他符号在大括号内不被解释.
反斜线符号() 是用来引用特殊符号. 例如:n 代表换行. 反斜线符号也被用来关闭"$"符号 , 引号,方括号和大括号的特殊含义 .
最好的学习方法就是边干边学,对于已经熟悉一种编程语言的人来说,用另一种新的语言来写程序解决问题,是很容易的事。所以大概了解一下基本语法后,就一边动手解决问题,一边查手册吧。
关于Tcl和Expect的语法,请参考Unix/Linux 平台任务的自动化相关部分。
引用: 例1:下面是一个telnet到指定的远程机器上自动执行命令的Expect脚本,该脚本运行时的输出如下:
# /usr/bin/expect sample_login.exp root 111111
spawn telnet 10.13.32.30 7001
Trying 10.13.32.30...
Connected to 10.13.32.30.
Escape character is '^]'.
accho console login: root
Password:
Last login: Sat Nov 13 17:01:37 on console
Sun Microsystems Inc. SunOS 5.9 May 2004
#
Login Successfully...
# uname -p
sparc
# ifconfig -a
lo0: flags=2001000849 mtu 8232 index 1
inet 127.0.0.1 netmask ff000000
eri0: flags=1000843 mtu 1500 index 2
inet 10.13.22.23 netmask ffffff00 broadcast 10.13.22.255
ether 0:3:ba:4e:4a:aa
# exit
accho console login:
Finished...
引用: 下面是该脚本的源代码:
# vi sample_login.exp:
proc do_console_login {login pass} {
set timeout 5
set done 1
set timeout_case 0
while ($done) {
expect {
"console login:" { send "$loginn" }
"Password:" { send "$passn" }
"#" {
set done 0
send_user "nnLogin Successfully...nn"
}
timeout {
switch -- $timeout_case {
0 { send "n" }
1 {
send_user "Send a return...n"
send "n"
}
2 {
puts stderr "Login time out...n"
exit 1
}
}
incr timeout_case
}
}
}
}
proc do_exec_cmd {} {
set timeout 5
send "n"
expect "#"
send "uname -pn"
expect "#"
send "ifconfig -an"
expect "#"
send "exitn"
expect "login:"
send_user "nnFinished...nn"
}
if {$argc"
send "o 202.199.248.11r"
expect {
"Connected" break
"refused" { sleep 10} ;
}
}
这里使用了我们在tcl语言中讲到的while和break命令,熟悉C的读者应该很容易看出
它的行为:不断地等待ftp>提示符,在提示符下面发送连接远端服务器的命令,如果服
务器回应是refused(连接失败),就等待10秒钟,然后开始下一次循环;如果是Conne
cted,那么就跳出循环执行下面的命令。sleep是expect的一个标准命令,表示暂停若干
秒钟。
expect还支持许多更复杂的进程控制方式,如fork,disconnect等等,你可以从手册
页面中得到详细的信息。另外,各种tcl运算符和流程控制命令,包括tcl函数也可以使
用。
有些读者可能会问,如果expect执行的话是否控制台输入不能使用了,答案是否定的
。expect命令运行时,如果某个等待的信息没有得到,那么程序会阻塞在相应的expect
语句处,这时,你在键盘上输入的东西仍然可以正常地传递到程序中去,其实对于那些
expect处理的信息,原则上你输入的内容仍然有效,只是expect的反映太快,总是抢在
你的前面“输入”就是了。知道了这一点之后,你就可能写一个expect脚本,让expect
自动处理来自fscki的那些恶心的yes/no选项(我们介绍过,这些yes/no其实完全是多余
的,正常情况下你除了选择yes之外什么也干不了)。
缺省下,expect在标准输出(你的终端上)输出所有来自应用程序的回应信息,你可
以用下面的两个命令重定向这些信息:
log_file [文件名]
这个命令让expect在你设置的文件中记录输出信息。必须注意,这个选项并不影响控
制台输出信息,不过如果你通过crond设置expect脚本在半夜运行的话,你就确实可能需
要这个命令来记录各种信息了。例如:
log_file expect.log
log_user 0/1
这个选项设置是否显示输出信息,设置为1时是缺省值,为0 的话,expect将不产生任
何输出信息,或者说简单地过滤掉控制台输出。必须记住,如果你用log_user 0关闭了
控制台输出,那么你同时也就关闭了对记录文件的输出。
这一点很让人困扰,如果你确实想要记录expect的输出却不想让它在控制台上制造垃
圾的话,你可以简单地把expect的输出重定向到/dev/null:
./test.exp > /dev/null
你可以象下面这样使用一对fork和disconnect命令。expect的disconnect命令将使得
相应的进程到后台执行,输入和输出被重定向到/dev/null:
if [fork]!=0 exit
disconnect
fork命令会产生出一个子进程,而且它产生返回值,如果返回的是0,说明这是一个子
进程,如果不为0,那么是父进程。因此,执行了fork命令之后,父进程死亡而子进程被
disconnect命令放到后台执行。注意disconnect命令只能对子进程使用。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/23273/showart_360756.html |
|