expect的基本用法

http://bbs.chinaunix.net/viewthread.php?tid=594417

一、概述



Expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。

Expect是不断发展的,随着时间的流逝,其功能越来越强大,已经成为系统管理员的的一个强大助手。Expect需要Tcl编程语言的支持,要在系统上运行Expect必须首先安装Tcl。





二、Expect工作原理



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



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



例子:

1、实现功能

下面我们分析一个响应chsh命令的脚本。我们首先回顾一下这个交互命令的格式。假设我们要为用户chavez改变登录脚本,要求实现的命令交互过程如下:


QUOTE:

# 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、下面是一个能用来实现自动执行该命令的Expect脚本:


CODE:

#!/usr/bin/expect

# Change a login shell to tcsh



set user [lindex $argv 0]

spawn chsh $user

expect "]:"

send "/bin/tcsh "

expect eof

exit




这个简单的脚本可以解释很多Expect程序的特性。和其他脚本一样首行指定用来执行该脚本的命令程序,这里是/usr/bin/expect。程序第一行用来获得脚本的执行参数(其保存在数组$argv中,从0号开始是参数),并将其保存到变量user中。



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



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



3、决定如何响应



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


CODE:

expect -re "/[(.*)]:"

if {$expect_out(1,string)!="/bin/tcsh"} {

send "/bin/tcsh" }

send " "

expect eof





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



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



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



4、使用超时



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


CODE:

#!/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]




脚本的第一部分首先是得到运行参数并将其保存到内部变量中。


CODE:

send_tty "$prompt: "

set timeout $tout

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]




这是脚本其余的内容。可以看到send_tty命令用来实现在终端上显示提示符字串和一个冒号及空格。set timeout命令设置后面所有的expect命令的等待响应的超时时间为$tout(-l参数用来关闭任何超时设置)。



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



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



一个有趣的事情是该脚本没有使用spawn命令。 该expect脚本会与任何调用该脚本的进程交互。



如果该脚本名为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"



5、一个更复杂的例子



下面我们将讨论一个更加复杂的expect脚本例子,这个脚本使用了一些更复杂的控制结构和很多复杂的交互过程。这个例子用来实现发送write命令给任意的用户,发送的消息来自于一个文件或者来自于键盘输入。


CODE:

#!/usr/bin/expect

# Write to multiple users from a prepared file

# or a message input interactively



if {$argc<2} {

send_user "usage: $argv0 file user1 user2 ... "

exit

}




send_user命令用来显示使用帮助信息到父进程(一般为用户的shell)的标准输出。


CODE:

set nofile 0

# get filename via the Tcl lindex function

set file [lindex $argv 0]

if {$file=="i"} {

set nofile 1

} else {

# make sure message file exists

if {[file isfile $file]!=1} {

send_user "$argv0: file $file not found. "

exit }}




这部分实现处理脚本启动参数,其必须是一个储存要发送的消息的文件名或表示使用交互输入得到发送消的内容的"i"命令。



变量file被设置为脚本的第一个参数的值,是通过一个Tcl函数lindex来实现的,该函数从列表/数组得到一个特定的元素。[]用来实现将函数lindex的返回值作为set命令的参数。



如果脚本的第一个参数是小写的"i",那么变量nofile被设置为1,否则通过调用Tcl的函数isfile来验证参数指定的文件存在,如果不存在就报错退出。



可以看到这里使用了if命令来实现逻辑判断功能。该命令后面直接跟判断条件,并且执行在判断条件后的{}内的命令。if条件为false时则运行else后的程序块。


CODE:

set procs {}

# start write processes

for {set i 1} {$i<$argc}

{incr i} {

spawn -noecho write

[lindex $argv $i]

lappend procs $spawn_id

}





后一部分使用spawn命令来启动write进程实现向用户发送消息。这里使用了for命令来实现循环控制功能,循环变量首先设置为1,然后因此递增。循
环体是最后的{}的内容。这里我们是用脚本的第二个和随后的参数来spawn一个write命令,并将每个参数作为发送消息的用户名。lappend命令
使用保存每个spawn的进程的进程ID号的内部变量$spawn_id在变量procs中构造了一个进程ID号列表。


CODE:

if {$nofile==0} {

setmesg [open "$file" "r"]

} else {

send_user "enter message,

ending with ^D: " }




最后脚本根据变量nofile的值实现打开消息文件或者提示用户输入要发送的消息。


CODE:

set timeout -1

while 1 {

if {$nofile==0} {

if {[gets $mesg chars] == -1} break

set line "$chars "

} else {

expect_user {

-re " " {}

eof break }

set line $expect_out(buffer) }



foreach spawn_id $procs {

send $line }

sleep 1}

exit


上面这段代码说明了实际的消息文本是如何通过无限循环while被发送的。while循环中的 if判断消息是如何得到的。在非交互模式下,下一行内容从消息文件中读出,当文件内容结束时while循环也就结束了。(break命令实现终止循环) 。

在交互模式下,expect_user命令从用户接收消息,当用户输入ctrl+D时结束输入,循环同时结束。 两种情况下变量$line都被用来保存下一行消息内容。当是消息文件时,回车会被附加到消息的尾部。

foreach循环遍历spawn的所有进程,这些进程的ID号都保存在列表变量$procs中,实现分别和各个进程通信。send命令组成了
foreach的循环体,发送一行消息到当前的write进程。while循环的最后是一个sleep命令,主要是用于处理非交互模式情况下,以确保消息
不会太快的发送给各个write进程。当while循环退出时,expect脚本结束。


三、参考资源


Expect软件版本深带有很多例子脚本,不但可以用于学习和理解expect脚本,而且是非常使用的工具。一般可以在/usr/doc
/packages/expect/example看到它们,在某些linux发布中有些expect脚本保存在/usr/bin目录下。



一些有用的expect脚本

autoexpect:这个脚本将根据自身在运行时用户的操作而生成一个expect脚本。它的功能某种程度上类似于在Emacs编辑器的键盘宏工具。一个自动创建的脚本可能是创建自己定制脚本的好的开始。

kibitz:这是一个非常有用的工具。通过它两个或更多的用户可以连接到同一个shell进程。

tkpasswd: 这个脚本提供了修改用户密码的GUI工具,包括可以检查密码是否是基于字典模式。这个工具同时是一个学习expect和tk的好实例。

### expect 工具基本用法 `Expect` 是一个用于自动化交互式应用程序的工具,它能够模拟用户与程序之间的交互。通过 `Expect` 脚本可以实现自动登录、执行命令、响应提示等功能,广泛应用于系统管理和自动化测试中。 #### 安装 Expect 在大多数 Linux 发行版中,`Expect` 并不是默认安装的工具。可以通过包管理器进行安装: ```bash # Debian/Ubuntu 系统 sudo apt-get install expect # Red Hat/CentOS 系统 sudo yum install expect ``` 安装完成后,可以通过编写 `.exp` 文件来运行 `Expect` 脚本。 #### 基本语法和结构 `Expect` 的脚本是基于 Tcl(Tool Command Language)语言编写的,因此它的语法与 Tcl 类似。以下是一些基本的 `Expect` 命令及其用途: - **spawn**:启动一个新的进程。 - **expect**:等待特定的输出。 - **send**:向进程中发送输入。 - **interact**:将控制权交还给用户,允许手动输入。 ##### 示例 1:自动登录 SSH 下面是一个简单的 `Expect` 脚本示例,用于自动登录到远程服务器并通过 SSH 执行命令: ```expect #!/usr/bin/expect set timeout 20 set host "example.com" set user "username" set password "password" spawn ssh $user@$host expect { "yes/no" { send "yes\r" exp_continue } "password:" { send "$password\r" } } expect "$ " send "ls -l\r" expect "$ " send "exit\r" interact ``` 在这个脚本中: - `spawn` 启动了 SSH 进程。 - `expect` 等待特定的提示信息,如“yes/no”或“password:”,并根据情况发送相应的响应。 - `send` 发送用户名、密码以及要执行的命令。 - `interact` 允许用户接管会话。 ##### 示例 2:处理多个可能的响应 `Expect` 支持同时处理多个可能的响应,并根据不同的情况进行不同的操作。例如,当等待某个命令的输出时,可能会遇到不同的结果,这时可以使用多分支 `expect` 结构: ```expect #!/usr/bin/expect spawn some_command expect { "success" { puts "Command succeeded." } "failure" { puts "Command failed." } timeout { puts "Command timed out." } } ``` 在这个例子中,`Expect` 会等待三种可能的结果:“success”、“failure” 或者超时,并根据实际情况执行相应的操作。 #### 控制流程和变量 `Expect` 支持变量的定义和使用,类似于 Tcl 语言。变量可以通过 `set` 命令定义,并通过 `$variable_name` 的方式引用。 ```expect set username "admin" set password "secret" spawn telnet some_host expect "Username:" send "$username\r" expect "Password:" send "$password\r" ``` 此外,`Expect` 还支持条件判断和循环结构,使得脚本更加灵活和强大。 #### 处理复杂交互 对于复杂的交互场景,`Expect` 提供了强大的功能来处理。例如,在某些情况下,程序可能会返回多种不同的提示,而这些提示需要不同的处理方式。此时可以使用 `exp_continue` 来继续等待其他字符串: ```expect expect { "string1" { # 执行某个操作 exp_continue } "string2" { # 执行另一个操作 } } ``` 在这个例子中,如果匹配到 `"string1"`,则执行相应的操作后继续等待其他字符串;如果匹配到 `"string2"`,则执行另一个操作并结束当前的 `expect` 块。 #### 调试和错误处理 为了调试 `Expect` 脚本,可以使用 `log_user` 命令来控制是否将输出记录到日志文件中: ```expect log_user 0 ;# 禁止输出到终端 spawn some_command log_user 1 ;# 恢复输出到终端 ``` 此外,还可以使用 `catch` 命令来捕获和处理异常: ```expect if [catch {spawn ssh user@host} error] { puts "Failed to spawn SSH session: $error" exit 1 } ``` #### 总结 `Expect` 是一个非常强大的工具,适用于各种需要自动化交互的场景。通过掌握其基本语法和常用命令,可以轻松地编写出高效的自动化脚本,提高工作效率。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值