(本文多年前发表在这里:http://www.51testing.com/html/38/225738-220039.html 现在重新整理一下)
Expect可用于处理交互式程序;Expect is a program that "talks" to other interactive programs according to a script.
首先看我的一个实例吧,我需要再本地运行两个build数据的脚本,然后将build好的数据copy到另一台数据服务器上,并且需要重启数据服务器的应用。开始我想简单地打通ssh通道,就不用输入密码也能完成scp和远程启动应用服务了(见:http://www.51testing.com /index.php?uid-225738-action-viewspace-itemid-208847),但不知因为什么原因,居然这次没弄成功ssh免登陆,所以我就学习用expect来实现交互式输入密码而不需要手工输入,这也顺便学习了一下expect(前阵子遇到过expect,当时理解不深入,也自己没成功使用)。还是看我的几行代码吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/usr/bin/expect -f set timeout 10 spawn /home/deploy/bin/startSourcingSafePayAccountSwitchDataInit interact spawn /home/deploy/bin/startSourcingSafePayCategorySwitchDataInit interact spawn ssh udas@172.29.61.155 "rm -rf /home/berkeleydb/sourcingSafePayAccountSwitch;rm -rf /home/berkeleydb/startSourcingSafePayCategorySwitch" expect "password:" send "udas\r" spawn scp -r /home/udasdata/sourcingSafePayAccountSwitch udas@172.29.61.155:/home/berkeleydb/ expect "password:" send "udas\r" interact spawn scp -r /home/dasdata/sourcingSafePayCategorySwitch udas@172.29.61.155:/home/berkeleydb/ expect "password:" send "udas\r" interact spawn ssh udas@172.29.61.155 "/home/deploy/bin/killws;/home/deploy/bin/startws" expect "password:" send "udas\r" interact |
Expect的关键命令: { Expect(作为语言,‘E’大写)有四个关键命令。}
第一个重要命令是spawn,用于创建新进程的 Expect 命令。它已经出现在我们使用过的每个示例中。在左边,它把路径拖到缺省外壳可执行文件并产生新实例。在这样做时,spawn 返回一个进程标识(在变量 spawn_id 中设置)。这可以在脚本中保存并设置,这给予了 expect 进程控制能力。
第二个是 expect(命令,小写‘e’),如果找到匹配,它搜索模式并执行命令。对于每条 expect 命令,可以有几个组,每个组都是由选项标志、与之匹配的模式以及要执行的命令或命令主体组成。缺省情况下,expect“侦听”SDTOUT 和 STDERR,直到找到匹配或 timeout 期满为止。
缺省情况下,使用 Tcl 的字符串匹配设施来匹配模式,它实现文件名替换,类似于 C 外壳模式匹配。-re 标志调用 regexp 匹配,-ex 表明必须是精确匹配,不带通配符或变量扩展。expect 的其它可选标志包括 -i 和 -nocase,前者表示要监控产生的进程,后者强迫在匹配之前将进程输出变为小写。对于完整的说明,在命令提示符下输入 man expect ,以查看 Expect 的系统手册页面文档。
第三个重要命令是 send,它用于为由 Expect 脚本正在监控的进程生成输入。send 合并选项以发送给指定的产生的过程(-i),缓慢地发送(-s,例如,在串行通信中,为了不使缓冲区溢出)以及其它几个选项。
1 2 3 4 5 6 7 8 9 10 11 |
#!/usr/bin/expect -f # wrapper to make passwd(1) be non-interactive # username is passed as 1st arg, passwd as 2nd set password [lindex $argv 1] spawn passwd [lindex $argv 0] expect "password:" send "$passwordr" expect "password:" send "$passwordr" expect eof |
上面是称为 carpal 的脚本,它也是来自源代码 Expect 分发版的另一个示例。
第四个命令是interact, 是 Expect 用来打开用户与产生进程之间通信的命令。-nobuffer 标志将与模式匹配的字符直接发送给用户。-re 告诉 interact 将接下来的模式用作标准正规表达式,‘.’是与输入时每个字符匹配的模式。在交互方式中,缺省情况下,Expect 的 STDOUT 和 STDERR 流的重定向也返回给用户。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#!/usr/local/bin/expect # Script. to enforce a 10 minute break # every half hour from typing - # Written for someone (Uwe Hollerbach) # with Carpal Tunnel Syndrome. # If you type for more than 20 minutes # straight, the script. rings the bell # after every character until you take # a 10 minute break. # Author: Don Libes, NIST # Date: Feb 26, '95 spawn $env(SHELL) # set start and stop times set start [clock seconds] set stop [clock seconds] # typing and break, in seconds set typing 1200 set notyping 600 interact -nobuffer -re . { set now [clock seconds] if {$now-$stop > $notyping} { set start [clock seconds] } elseif {$now-$start > $typing} { send_user "07" } set stop [clock seconds] } |
使用 Expect 可以完成哪些任务?
当脚本调用交互式程序时,缺省情况下,Expect 拦截所有输入和输出(STDIN、STDOUT 和 STDERR)。这允许 Expect 搜索与程序输出匹配的模式,并将输入发送到产生的进程,以模拟用户交互。另外,Expect 可以将进程的控制传递给用户(如果这样指示的话),或者根据请求控制。
这些特性不仅使 Expect 对于公共管理任务变得非常有用,而且证实了 Expect 有益于构建测试脚本,以在程序开发期间执行 I/O 验证。
下面是网上另外的实例:
下面是一个telnet到指定的远程机器上自动执行命令的Expect脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
proc do_console_login {login pass} { set timeout 5 set done 1 set timeout_case 0 while ($done) { expect { "console login:" { send "$login\n" } "Password:" { send "$pass\n" } "#" { set done 0 send_user "\n\nLogin Successfully...\n\n" } 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 -p\n" expect "#" send "ifconfig -a\n" expect "#" send "exit\n" expect "login:" send_user "\n\nFinished...\n\n" } if {$argc<2} { puts stderr "Usage: $argv0 login passwaord.\n " exit 1 } set LOGIN [lindex $argv 0] set PASS [lindex $argv 1] spawn telnet 10.13.32.30 7001 do_console_login $LOGIN $PASS do_exec_cmd close exit 0 |
上面的脚本只是一个示例,实际工作中,只需要重新实现do_exec_cmd函数就可以解决类似问题了。
在上面例子中,还可以学习到以下Tcl的语法:
1. 命令行参数
$argc,$argv 0,$argv 1 ... $argv n
if {$argc<2} {
puts stderr "Usage: $argv0 login passwaord.\n "
exit 1
}
2. 输入输出
puts stderr "Usage: $argv0 login passwaord.\n "
3. 嵌套命令
set LOGIN [lindex $argv 0]
set PASS [lindex $argv 1]
4. 命令调用
spawn telnet 10.13.32.30 7001
5. 函数定义和调用
proc do_console_login {login pass} {
..............
}
6. 变量赋值
set done 1
7. 循环
while ($done) {
................
}
8. 条件分支Switch
switch -- $timeout_case {
0 {
...............
}
1 {
...............
}
2 {
...............
}
}
9. 运算
incr timeout_case
此外,还可以看到 Expect的以下命令:
send
expect
send_user
可以通过-d参数调试Expect脚本。。
上面的参考资料:http://blog.opendigest.org/show-289-1.html
以前也很苦恼expect , 不好用啊,今天发现用python 很容易就解决这个问题了
#!/usr/bin/python -tt
import paramiko
hostname = '192.168.5.51'
port = 22
username = 'root'
password = 'password'
if __name__ == "__main__":
paramiko.util.log_to_file('paramiko.log')
s = paramiko.SSHClient()
s.load_system_host_keys()
s.connect(hostname, port, username, password)
stdin, stdout, stderr = s.exec_command('ifconfig')
print stdout.read()
s.close()
嗯 挺好的~ 我用python后,也没用shell中的expect了;现在,我用Fabric. (没细看,不清楚它后面的登陆是不是用你举例的那么做)
http://smilejay.cn/2013/03/fabric-introduction/