目录]
1.摘要
2.关键字
3.简介
4.Expect综述
5.callback
6.passwd 和一致性检查
7.rogue 和伪终端
8.ftp
9.fsck
10.多进程控制:作业控制
11.交互式使用Expect
12.交互式Expect编程
13.非交互式程式的控制
14.Expect的速度
15.安全方面的考虑
16.Expect资源
17.参考书籍
1.[摘要]
现代的Shell对程式提供了最小限度的控制(开始,停止,等等),而把交互的特性留给了用户。这意味着有些程式,你不能非交互的运行,比如说 passwd。有一些程式能非交互的运行,但在非常大程度上丧失了灵活性,比如说fsck。这表明Unix的工具构造逻辑开始出现问题。Expect恰恰填补了其中的一些裂痕,解决了在Unix环境中长期存在着的一些问题。
Expect使用Tcl作为语言核心。不仅如此,不管程式是交互和还是非交互的,Expect都能运用。这是个小语言和Unix的其他工具配合起来产生强大功能的经典例子。
本部分教程并不是有关Expect的实现,而是关于Expect语言本身的使用,这主要也是通过不同的脚本描述例子来体现。其中的几个例子还例证了Expect的几个新特征。
2.[关键字]
Expect,交互,POSIX,程式化的对话,Shell,Tcl,Unix;
3.[简介]
一个叫做fsck的Unix文件系统检查程式,能从Shell里面用-y或-n选项来执行。 在手册[1]里面,-y选项的定义是象这样的。
“对于fsck的所有问题都假定一个“yes”响应;在这样使用的时候,必须特别的小心,因为他实际上允许程式无条件的继续运行,即使是遇见了一些非常严重的错误”
相比之下,-n选项就安全的多,但他实际上几乎一点用都没有。这种接口非常的糟糕,不过却有许多的程式都是这种风格。 文件传输程式ftp有一个选项能禁止交互式的提问,以便能从一个脚本里面运行。但一旦发生了错误,他没有提供的处理措施。
Expect是个控制交互式程式的工具。他解决了fsck的问题,用非交互的方式实现了所有交互式的功能。Expect不是特别为fsck设计的,他也能进行类似ftp的出错处理。
fsck和ftp的问题向我们展示了象sh,csh和别的一些shell提供的用户接口的局限性。 Shell没有提供从一个程式读和象一个程式写的功能。这意味着shell能运行fsck但只能以牺牲一部分fsck的灵活性做代价。有一些程式根本就不能被执行。比如说,如果没有一个用户接口交互式的提供输入,就没法运行下去。其他更有象Telnet,crypt,su,rlogin等程式无法在shell脚本里面自动执行。更有非常多其他的应用程式在设计是也是需求用户输入的。
Expect被设计成专门针和交互式程式的交互。一个Expect程式员能写一个脚本来描述程式和用户的对话。接着Expect程式能非交互的运行“交互式”的程式。写交互式程式的脚本和写非交互式程式的脚本相同简单。Expect还能用于对对话的一部分进行自动化,因为程式的控制能在键盘和脚本之间进行转换。
bes[2]里面有周详的描述。简单的说,脚本是用一种解释性语言写的。(也有C和C++的Expect库可供使用,但这超出了本文的范围).Expect提供了创建交互式进程和读写他们的输入和输出的命令。 Expect是由于他的一个同名的命令而命名的。
Expect语言是基于Tcl的。Tcl实际上是个子程式库,这些子程式库能嵌入到程式里从而提供语言服务。 最终的语言有点象一个典型的 Shell语言。里面有给变量赋值的set命令,控制程式执行的if,for,continue等命令,还能进行普通的数学和字符串操作。当然了,还能用exec来调用Unix程式。所有这些功能,Tcl都有。Tcl在参考书籍 Outerhour[3][4]里有周详的描述。
Expect是在Tcl基础上创建起来的,他还提供了一些Tcl所没有的命令。spawn命令激活一个Unix程式来进行交互式的运行。 send命令向进程发送字符串。expect命令等待进程的某些字符串。 expect支持正规表达式并能同时等待多个字符串,并对每一个字符串执行不同的操作。 expect还能理解一些特别情况,如超时和遇见文件尾。
expect命令和Tcl的case命令的风格非常相似。都是用一个字符串去匹配多个字符串。(只要有可能,新的命令总是和已有的Tcl命令相似,以使得该语言保持工具族的继承性)。下面关于expect的定义是从手册[5]上摘录下来的。
expect patlist1 action1 patlist2 action2.....
该命令一直等到当前进程的输出和以上的某一个模式相匹配,或等 到时间超过一个特定的时间长度,或等到遇见了文件的结束为止。
如果最后一个action是空的,就能省略他。
每一个patlist都由一个模式或模式的表(lists)组成。如果有一个模式匹配成功,相应的action就被执行。执行的结果从expect返回。
被精确匹配的字符串(或当超时发生时,已读取但未进行匹配的字符串)被存贮在变量expect_match里面。如果patlist是eof 或timeout,则发生文件结束或超时时才执行相应的action.一般超时的时值是10秒,但能用类似"set timeout 30"之类的命令把超时时值设定为30秒。
下面的一个程式段是从一个有关登录的脚本里面摘取的。abort是在脚本的别处定义的过程,而其他的action使用类似和C语言的Tcl原语。
expect "*welcome*" break
"*busy*" {print busy;continue}
"*failed*" abort
timeout abort
模式是通常的C Shell风格的正规表达式。模式必须匹配当前进程的从上一个expect或interact开始的所有输出(所以统配符*使用的非常)的普遍。不过,一旦输出超过2000个字节,前面的字符就会被忘记,这能通过设定match_max的值来改动。
expect命令确实体现了expect语言的最佳和最坏的性质。特别是,expect命令的灵活性是以经常出现令人迷惑的语法做代价。除了关键字模式 (比如说eof,timeout)那些模式表能包括多个模式。这确保提供了一种方法来区分他们。不过分开这些表需要额外的扫描,如果没有恰当的用 ["]括起来,这有可能会把和当成空白字符。由于Tcl提供了两种字符串引用的方法:单引和双引,情况变的更糟。(在Tcl里面,如果不会出现二义性话,没有必要使用引号)。在expect的手册里面,更有一个独立的部分来解释这种复杂性。幸运的是:有一些非常好的例子似乎阻止了这种抱怨。不过,这个复杂性非常有可能在将来的版本中再度出现。为了增强可读性,在本文中,提供的脚本都假定双引号是足够的。
字符能使用反斜杠来独立的引用,反斜杠也被用于对语句的延续,如果不加反斜杠的话,语句到一行的结尾处就结束了。这和Tcl也是一致的。Tcl在发现有开的单引号或开的双引号时都会继续扫描。而且,分号能用于在一行中分割多个语句。这乍听起来有点让人困惑,不过,这是解释性语言的风格,不过,这确实是Tcl的不太漂亮的部分。
5.[callback]
令人非常惊讶的是,一些小的脚本怎么的产生一些有用的功能。下面是个拨电话号码的脚本。他用来把收费反向,以便使得长途电话对计算机计费。这个脚本用类似“expect callback.exp 12016442332”来激活。其中,脚本的名字便是callback.exp,而+1(201)644-2332是要拨的电话号码。
#first give the user some time to logout
exec sleep 4
spawn tip modem
expect "*connected*"
send "ATD [index $argv 1] "
#modem takes a while to connect
set timeout 60
expect "*CONNECT*"
第一行是注释,第二行展示了怎么调用没有交互的Unix程式。sleep 4会使程式阻塞4秒,以使得用户有时间来退出,因为modem总是会回叫用户已使用的电话号码。
下面一行使用spawn命令来激活tip程式,以便使得tip的输出能够被expect所读取,使得tip能从send读输入。一旦tip说他已连接上,modem就会需求去拨打大哥电话号码。(假定modem都是贺氏兼容的,不过本脚本能非常容易的修改成能适应别的类型的modem)。不论发生了什么,expect都会终止。如果呼叫失败,expect脚本能设计成进行重试,但这里没有。如果呼叫成功,getty会在expect退出后检测到 DTR,并且向用户提示loging:。(实用的脚本往往提供更多的错误检测)。
这个脚本展示了命令行参数的使用,命令行参数存贮在一个叫做argv的表里面(这和C语言的风格非常象)。在这种情况下,第一个元素就是电话号码。方括号使得被括起来的部分当作命令来执行,结果就替换被括起来的部分。这也和C Shell的风格非常象。
这个脚本和一个大约60K的C语言程式实现的功能相似。
阅读(844) | 评论(0) | 转发(0) |