Shell脚本学习之expect命令(2)

三、Expect工作原理

从最简单的层次来说,Expect的工作方式象一个通用化的Chat脚本工具。Chat脚本最早用于UUCP网络内,以用来实现计算机之间需要建立连接时进行特定的登录会话的自动化。

Chat脚本由一系列expect-send对组成:expect等待输出中输出特定的字符,通常是一个提示符,然后发送特定的响应。例如下面的 Chat脚本实现等待标准输出出现Login:字符串,然后发送somebody作为用户名;然后等待Password:提示符,并发出响应 sillyme。

引用:

Login: somebody Password: sillyme

Expect最简单的脚本操作模式本质上和Chat脚本工作模式是一样的。

例子:

1、实现功能

下面我们分析一个响应chsh命令的脚本。我们首先回顾一下这个交互命令的格式。

假设我们要为用户chavez改变登录脚本,要求实现的命令交互过程如下:

# chsh chavez
Changing the login shell for chavez
Enter the new value, or press return for the default
Login Shell [/bin/bash]: /bin/tcsh
#

可以看到该命令首先输出若干行提示信息并且提示输入用户新的登录shell。我们必须在提示信息后面输入用户的登录shell或者直接回车不修改登录shell。

2、实现自动执行

#!/usr/bin/expect
# Change a login shell to tcsh

set user [lindex $argv 0]
spawn chsh $user
expect "]:"
send "/bin/tcsh "
expect eof

exit

说明:

(1)首行指定用来执行该脚本的命令程序,这里是/usr/bin/expect。

(2)程序第一行用来获得脚本的执行参数(其保存在数组$argv中,从0号开始是参数),并将其保存到变量user中。

(3)第二个参数使用expect的spawn命令来启动脚本和命令的会话,这里启动的是chsh命令,实际上命令是以衍生子进程的方式来运行的。

(4)随后的expect和send命令用来实现交互过程。脚本首先等待输出中出现]:字符串,一旦在输出中出现chsh输出到的特征字符串(一般特征 字符串往往是等待输入的最后的提示符的特征信息)。对于其他不匹配的信息则会完全忽略。当脚本得到特征字符串时,expect将发送/bin/tcsh和 一个回车符给chsh命令。最后脚本等待命令退出(chsh结束),一旦接收到标识子进程已经结束的eof字符,expect脚本也就退出结束。

3、决定如何响应

系统管理员往往有这样的需求,希望根据当前的具体情况来以不同的方式对一个命令进行响应。我们可以通过后面的例子看到expect可以实现非常复杂的条件响应,而仅仅通过简单的修改预处理脚本就可以实现。

下面的例子是一个更复杂的expect-send例子:

expect -re "\[(.*)]:"
if {$expect_out(1,string)!="/bin/tcsh"} {
send "/bin/tcsh" }
send " "
expect eof

说明:

(1)第一个expect命令现在使用了-re参数,这个参数表示指定的的字符串是一个正则表达式,而不是一个普通的字符串。对于上面这个例子里是查找一个左方括号字符(其必须进行三次逃逸(escape),因此有三个符号,因为它对于expect和正则表达时来说都是特殊字符)后面跟有零个或多个字符,最后是一个右方括号字符。这里.*表示表示一个或多个任意字符,将其存放在()中是因为将匹配结果存放在一个变量中以实现随后的对匹配结果的访问。

(2)当发现一个匹配则检查包含在[]中的字符串,查看是否为/bin/tcsh。如果不是则发送/bin/tcsh给chsh命令作为输入,如果是则仅仅发送一个回车符。这个简单的针对具体情况发出不同相响应的小例子说明了expect的强大功能。

(3)在一个正则表达时中,可以在()中包含若干个部分并通过expect_out数组访问它们。各个部分在表达式中从左到右进行编码,从1开始(0包含有整个匹配输出)。()可能会出现嵌套情况,这这种情况下编码从最内层到最外层来进行的。

4、使用超时

下一个expect例子中将阐述具有超时功能的提示符函数。这个脚本提示用户输入,如果在给定的时间内没有输入,则会超时并返回一个默认的响应。这个脚本接收三个参数:提示符字串,默认响应和超时时间(秒)。

#!/usr/bin/expect
# Prompt function with timeout and default.

#脚本的第一部分首先是得到运行参数并将其保存到内部变量中
set prompt [lindex $argv 0]
set def [lindex $argv 1]
set response $def
set tout [lindex $argv 2]

send_tty "$prompt: "
#send_tty命令用来实现在终端上显示提示符字串和一个冒号及空格
set timeout $tout
#set timeout命令设置后面所有的expect命令的等待响应的超时时间为$tout(-l参数用来关闭任何超时设置)。
expect " " {
set raw $expect_out(buffer)

# remove final carriage return
set response [string trimright "$raw" " "]
}
if {"$response" == "} {set response $def}
send "$response "

# Prompt function with timeout and default.
set prompt [lindex $argv 0]
set def [lindex $argv 1]
set response $def
set tout [lindex $argv 2]

说明:

(1)send_tty命令用来实现在终端上显示提示符字串和一个冒号及空格。

(2)set timeout命令设置后面所有的expect命令的等待响应的超时时间为$tout(-l参数用来关闭任何超时设置)。

(3)然后expect命令就等待输出中出现回车字符。如果在超时之前得到回车符,那么set命令就会将用户输入的内容赋值给变脸raw。随后的命令将用户输入内容最后的回车符号去除以后赋值给变量response。

(4)如果response中内容为空则将response值置为默认值(如果用户在超时以后没有输入或者用户仅仅输入了回车符)。最后send命令将response变量的值加上回车符发送给标准输出。

注意:

(1)该脚本没有使用spawn命令。

(2)该expect脚本会与任何调用该脚本的进程交互。

(3)如果该脚本名为prompt,那么它可以用在任何C风格的shell中。

% set a='prompt "Enter an answer" silence 10'
Enter an answer: test

% echo Answer was "$a"
Answer was test

prompt设定的超时为10秒。如果超时或者用户仅仅输入了回车符号,echo命令将输出

Answer was "silence"

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/4d21e88bf7fde00ac72f360d0d64aea8.html