set timeout 10
spawn ftp 172.16.107.9
expect "Connect"
send "kfcb\r"
expect "Password:"
send "kfcb\r"
expect "logged in"
send "cd usr/\r"
expect "successful"
send "get bal.txt\r"
expect "complete"
send "bye\r"
expect eof
现在我们看一看我们还能够如何改进我们的expect脚本。ftp命令可能会失败,比如远端的机器可能会无法提供服务,或者在启动ftp命令时本地机器发生问题。为了处理这一类的问题,我们可以使用expect的timeout选项来设置超时脚本自动退出:
expect {
timeout exit
Connect ...
}
注意这里面使用的花括号,它的含义是使用一组并列表达式。如果使用下面的指令对:
expect timeout
exit
那么由于expect脚本是顺序执行的,那么当程序执行到这个expect的时候就会阻塞,所以程序会一直等待到timeout然后退出。并列表达式则是相当于switch的行为,只要列出的几项内容有一项得到满足,expect命令就得到满足,于是程序可以正常执行。
我们可以看看用tcl能够对我们的expect脚本提供什么帮助。我们可以设置让expect脚本不断地连接远端服务器的服务,直到正常建立连接开始。为此,我们可以把建立连接的命令放在一个循环里面,并且根据回应的不同自动选择重新输入命令还是继续执行。sleep是expect的一个标准命令,表示暂停若干秒钟。
spawn ftp
while {1} {
expect "ftp>"
send "o 172.16.107.9\r"
expect {
"Connected" break
"refused" { sleep 10 } ;
}
}
有些读者可能会问,如果expect执行的话是否控制台输入不能使用了,答案是否定的。expect命令运行时,如果某个等待的信息没有得到,那么程序会阻塞在相应的expect语句处,这时,你在键盘上输入的东西仍然可以正常地传递到程序中去,其实对于那些expect处理的信息,原则上你输入的内容仍然有效,只是expect的反映太快,总是抢在你的前面“输入”就是了。
缺省情况下,expect在标准输出(你的终端上)输出所有来自应用程序的回应信息,你可以用下面的两个命令重定向这些信息:log_file [文件名] 。这个命令让expect在你设置的文件中记录输出信息。必须注意,这个选项并不影响控制台输出信息。例如:log_file expect.log 。log_user 0/1 这个选项设置是否显示输出信息,设置为1时是缺省值,为0的话,expect将不产生任何输出信息,或者说简单地过滤掉控制台输出。必须记住,如果你用log_user 0关闭了控制台输出,那么你同时也就关闭了对记录文件的输出。此外还需要注意,log_user 0关闭的只是应用程序的回应信息,而没有关闭标准输出,也就是说程序中使用send_user命令输出的内容仍然会输出到屏幕和文件。这一点很让人困扰,如果你确实想要记录expect的输出却不想让它在控制台上制造垃圾的话,你可以简单地把expect的输出重定向到/dev/null。
经过改进后的expect代码:
#!/usr/expect/bin/expect -f
#记录执行日志
log_file $env(HOME)/log/exp.log
#0-关闭日志/1-打开日志
log_user 1
set timeout 30
set IP 172.16.107.99
set try_times 0
spawn ftp
while {1} {
expect "ftp>"
if $try_times>=3 {
send_user "Try 3 times, can't connected!"
exit 1
}
send "o $IP\r"
expect {
"Connected" break
timeout {
sleep 10
incr try_times 1
}
"refused" {
sleep 10
incr try_times 1
}
}
}
send "kfcb\n"
expect "Password:"
send "kfcb\n"
expect "logged in"
send "bin\n"
send "cd usr/\n"
expect "successful"
send "get bal.txt\n"
expect "complete"
send "bye\n"
expect eof