新手求教 多个shell expectt 一起跑的问题

查看: 4255|回复: 8
战士, 积分 957, 距离下一级还需 543 积分
精华0帖子威望0 点积分957 点注册时间最后登录
& & 刚接触游戏几天,感觉不错非常符合个人口味,现在刚打过简单模式的黑咒岛二周目的觉醒boss,一共只打到一件武器两件防具。请问几个问题:
1 看到有位大大说逢魔的螺旋有个能刷lv3的防具的宝箱,请问这个宝箱的位置?
2 觉醒的boss掉装备吗?我怎么没看到啊,只拣到他的角和爪。另外难度对刷道具有影响吗?可不可以简单刷满了再去hard?
3 lv3的武器装备明显没有lv2的好看啊,lv2的大镰刀多霸气,lv3的武器是不是都是一个样子的,每样一种,就像腐龙武器一样?
& &话说二周目打到最后的时候以为还是红毛神呢,结果没想到变成了一周目的自己,连装备都一样。真心感动啊!再联想new game时为什么都要以红毛开头的缘故,原来就是讲轮回的啊。不过上一周目我是自杀的结局,最后胖变成了我在渔村生活,这样看二周目的结局就对不上了,请大大们给予答疑。
& &黑咒岛的结局是什么意思?一周目救出了那个男的和他的女友走了,他们谁是觉者谁是胖啊?这一幕当中看到在地上挣扎的奥尔加有种莫名的喜感
为什么二周目二周目变身了再杀一次最后出现一个男的,这个男的又是谁啊?感觉和觉者身上的心脏有关系。日语英语都是半吊子,求真相帝解惑。
佣兵, 积分 407, 距离下一级还需 343 积分
精华0帖子威望0 点积分407 点注册时间最后登录
黑岛一不小心一周目通关的路过,大陆还没通关………
战士, 积分 957, 距离下一级还需 543 积分
精华0帖子威望0 点积分957 点注册时间最后登录
都没有人了啊,还有一个问题,我的小胖现在是斗士120多级,怎么升一级才加5点血?我看资料上是20点啊,是不是和hard模式有关?
佣兵, 积分 702, 距离下一级还需 48 积分
精华0帖子威望0 点积分702 点注册时间最后登录
1.逢魔螺旋就是打第1个浑身带刺的巨人塔
论坛有视频,感谢UP主...
2.BOSS会随机掉LV3的装备,不过几率不太大,我刷过几十把最终BOSS,从来没出过腿甲。最后还是刷封魔螺旋的宝箱出的...在上2层后,不要急着往3层跑,往回走,有道门,进去后经过一段小桥,会有个LV3防具的箱子。下面是逢魔螺旋的地图...上面标明几号宝箱出什么东西的。至于难度跟出装我感觉没啥关系...
3.剧情我感觉对反派交代的还是太少啦,想想黑魂的背景故事要有意思的多...
战士, 积分 957, 距离下一级还需 543 积分
精华0帖子威望0 点积分957 点注册时间最后登录
感谢小丑哥的回答,这是我最喜欢的反派了,甩蝙蝠侠几条街的感觉。黑魂我也是死忠,有时间要交流啊
Graven Trevelyan
审判者, 积分 17967, 距离下一级还需 2033 积分
精华1帖子威望1 点积分17968 点注册时间最后登录
故事么就是大门原来是个觉者,叫阿修,爱上了一个曾经救助过他的觉者格蕾蒂。
但是格蕾蒂挑战界王失败,被洗脑变成了新的红龙。
当阿修以心上人为目标磨练自己,并挑战红龙的时候,发觉这条龙就是他深爱的格蕾蒂。同时知道了这个世界关于觉者,红龙,界王的无限循环。
然后他就疯了,想破坏这个世界的循环,所以无意发现了黑岛,并得到了力量成为大门。
奥尔加是被格蕾蒂的pawn附体,格蕾蒂的pawn长得和她一模一样。
战士, 积分 957, 距离下一级还需 543 积分
精华0帖子威望0 点积分957 点注册时间最后登录
原来如此,感觉黑岛的怪二周目以后随便拿出来几个放到半岛,觉者们就都出不了门了,完全能切断轮回的样子。不过对游戏的难度感到失望,一直玩的简单转换到困难后觉得和简单没什么区别啊,觉醒boss还是近乎虐杀,倒是一些小怪的变态组合更难搞定一些。
其实阿修当年刷几件武器防具就可以回去当无敌界王了,何必当大门被后来人刷呢,悲剧
公民, 积分 251, 距离下一级还需 49 积分
精华0帖子威望0 点积分252 点注册时间最后登录
本帖最后由 iwangbin 于
10:24 编辑
估计阿修当年面对红龙的时候,发觉这条龙就是他深爱的格蕾蒂,而红龙认为他的爱人是光头商人,阿修面对这狗血的剧情当场疯掉了吧。
战士, 积分 957, 距离下一级还需 543 积分
精华0帖子威望0 点积分957 点注册时间最后登录
iwangbin 发表于
估计阿修当年面对红龙的时候,发觉这条龙就是他深爱的格蕾蒂,而红龙认为他的爱人是光头商人,阿修面对这狗 ...
不小心跑剧情的一周目很容易悲剧,不过我一周目就很讨厌他,而且一周目没钱基本他卖的东西我也买不起,再加上他的任务很狗血导致我很讨厌他。任务时直接上交了一家三口的上诉书。所以奸商必须终身监禁,他的作用就是护符而已,对于现在钱已经浮云的多周目玩家来说,监狱才是最适合他的地方
Powered by
扫描二维码
下载 A9VG 客户端(iOS, Android)跑步机的参数有哪些不准确?新人关于使用跑步机的几个问题求教
本人刚刚开始健身减肥。身高175,1月测体重曾达98kg,2月开始运动减肥,开始是每天走15000步,后来逐渐增加了跑。目前体重已经降到82kg。目前的运动量在每天步。其中除了快走外,增加了跑步机跑步,一开始每次5公里(跑步机计数),一周3、4次,现在每次6公里或10公里,每周5次。我发现跑步机在计时上是准确的,但速度似乎很不准确,我在户外跑道或公路上行走,速度最多能到6.5公里/小时。但在跑步机上要设成7.2公里/小时才能达到户外走的强度。考虑到跑步机的速度应该与电机的转速和跑带的行程相关,电机转速一般会比较准确,应该是跑带的行程不准。但这样一来,跑步机的行程记录应该也不准确。不知道各位达人是否确切了解。其他的问题还有,从减肥的效率而言,我有3种运动方案:(1)我在跑台上是先跑35~40分钟(约5~6公里)然后再走1个小时(约7公里)。(2)先走1个小时,再跑40分钟。(3)直接跑60~70分钟(10公里)。哪一种效率更高些?其次,将近4个月的运动健身,应该说减肥效果还是不错的,但我现在运动量已经是个人觉得比较极限的程度了,不是量大的问题,而是每天能投入健身的时间问题。毕竟还要工作和生活。从98kg到95kg,我觉得还是比较轻松的,从95到90开始运动量增加,到现在接近每天30000步。我担心要降到66kg~70kg体重,运动量要增加到无法承受?即使能保持这个速度达到目标体重,运动量降下来是否会反弹?说到这个,我觉得很奇怪的是,我运动后称体重会降低到一个数字,然后休息1~2小时后体重会回升将近0.5kg。期间我最多喝了一点茶水(东方树叶)。如果单纯是补充水分后体重恢复倒也可以理解,但为什么脂肪率会回升呢?按理说补充了水分,脂肪率应该下降才对。这个我不能理解,此外,我晚上一般吃一点水果,也就200g西瓜或2个橙子,再加20个左右樱桃西红柿之类的。但体重会增加超过1kg,我不明白为什么补充的食物重量会低于增加的体重。以上几个疑问请各位高人和跑友指教,不胜感激!
没有人赐教吗?
发自手机虎扑 m.hupu.com
昨天尝试了一下先跑10km再走,觉得这种方式不好,主要是跑完之后感觉比较疲劳,无法保持比较快的速度走,步子容易太大。
跑步机没使用过所以不好给意见我也坐等了解学习学习
今天已经看到了80.9kg的重量,继续向着目标前进。我觉得有氧跑前做肌肉训练效果一般,反而使我在跑步时略感不适,跑10km里最后是硬坚持下来的。最近的经验体会是,每天最好不要将运动时间集中,而是至少分为白天和傍晚(夜晚)两个时段,然后多吃点纤维含量高糖分低的食物,多喝水,提高新陈代谢。如果只是白天运动,那么午餐后就不能吃东西了,包括水果。
只知道不准确,在跑步机上轻而易举三分钟1000米,上学那会儿累的吐血也没跑到过三分钟以内。然而并不知道什么原因造成的。
引用6楼 @ 发表的:
只知道不准确,在跑步机上轻而易举三分钟1000米,上学那会儿累的吐血也没跑到过三分钟以内。然而并不知道什么原因造成的。
我觉得问题要分两个方面看,跑台的减震和支撑作用远远好于户外的跑道。感觉非常明显,所以在跑步机上跑肯定比户外跑道跑轻松。另一方面是跑步机本身的准确性问题。目前我认为时间肯定是准的,跑1个小时就是1个小时。速度我觉得还是看自己觉得是否舒服适应,我跑主要是为了减肥,所以是匀速跑,跑快了慢了都不好。所以里程什么的只是参考而已。
今年开始锻炼以来,我一直坚持带护膝和护踝。带护踝是因为我跟腱有伤,几年前锻炼被迫中止就是因为跟腱剧痛不能坚持跑步。这次一直带护踝觉得效果不错。坚持运动四个月了。现在一直还可以跑。但是一直带护踝和护膝似乎也有问题,平时不带的时候总感觉似乎膝盖和脚踝有些无力支撑。
户外跑是把身体提高向前进,而跑步机实际上只需要提高身体就可以了。我以前也有这个疑问,户外跑道七八公里累成狗,跑步机随便到10。联系楼主开始多在跑步机上跑,慢慢转移点运动量到普通道路。另外看到的健身APP一般推荐跑前做10分钟无氧,再跑减脂效果更好一点,仅供参考最后说一句,楼主加油,为了更好的自己
跑步机的运动消耗就实际运动消耗的一半吧
引用9楼 @ 发表的:
户外跑是把身体提高向前进,而跑步机实际上只需要提高身体就可以了。我以前也有这个疑问,户外跑道七八公里累成狗,跑步机随便到10。
联系楼主开始多在跑步机上跑,慢慢转移点运动量到普通道路。
另外看到的健身APP一般推荐跑前做10分钟无氧,再跑减脂效果更好一点,仅供参考
最后说一句,楼主加油,为了更好的自己
多谢指点,不过我觉得您说跑步机跑只是提高身体是不对的,跑步机跑带是向后运动的,如果自身不提供向前的速度匹配是无法稳定在跑步机上的。如果只是向上抬高身体,那么应该无论设置什么速度,消耗都是一样的,但显然事实并非如此。
不过跑步机跑肯定没有很大的风阻,路面的反震对腿的冲击也不一样,所以跑步机跑肯定消耗比户外跑小这是没有问题的。
我目前选择在跑步机上跑主要是两个考虑,一个是体重较大,还有老伤,如果运动受伤导致被迫休息就得不偿失了。如果体重减轻到70kg左右,我觉得会考虑户外跑。
10分钟无氧我觉得比本区有人提到的一个小时无氧再一个小时有氧我觉得更合理些,至少对我如此,我会去试试。再次多谢。
[&此帖被装甲战熊在 07:53修改&]
引用10楼 @ 发表的:
跑步机的运动消耗就实际运动消耗的一半吧
肯定低,但我觉得实际消耗不止一半。
这两天跑了2次户外5km,用时基本都是31分。1天是先跑5km再走10km,1天是先走5km,再跑5km,再走5km。我前一段是在跑步机上跑10km,走5km。感觉与跑步机相比,进入呼吸极点要快,地形起伏也有影响。但从跑完当天测量体重和体脂等数据看,消耗与跑步机跑差别比想象中要小。
您需要登录后才可以回复,请或者
478人参加团购348.00元&549.00元
38人参加团购435.00元&999.00元
391人参加团购628.00元&1299.00元
1366人参加团购719.00元&1399.00元
83人参加团购9.90元&49.00元
126人参加团购478.00元&769.00元
194人参加团购259.00元&699.00元
102人参加团购799.00元&1599.00元
18人参加团购69.00元&158.00元
12人参加团购159.00元&339.00元
47人参加团购99.00元&329.00元
494人参加团购318.00元&969.00元博客访问: 2979485
博文数量: 610
博客积分: 10067
博客等级: 上将
技术积分: 4078
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
本文出自: 作者: 葫芦娃 翻译 ( 10:00:00)[版权声明]      Copyright(c) 1999        本教程由*葫芦娃*翻译,并做了适当的修改,可以自由的用于非商业目的。   但Redistribution时必须拷贝本[版权声明]。       [BUG]   有不少部分,翻译的时候不能作到"信,达"。当然了,任何时候都没有做到"雅",希望各位谅解。 [原著]     Don Libes: National Institute of Standards and Technology     libes@cme.nist.gov [目录]      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,U 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*"        {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 "来激活。其中,脚本的名字便 是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语言程序实现的功能相似。      6.[passwd和一致性检查]   在前面,我们提到passwd程序在缺乏用户交互的情况下,不能运行,passwd会忽略I/O重定向,也不能嵌入到管道里 边以便能从别的程序或者文件里读取输入。这个程序坚持要求真正的与用户进行交互。因为安全的原因,passwd被设计 成这样,但结果导致没有非交互式的方法来检验passwd。这样一个对系统安全至关重要的程序竟然没有办法进行可靠的 检验,真实具有讽刺意味。   passwd以一个用户名作为参数,交互式的提示输入密码。下面的expect脚本以用户名和密码作为参数而非交互式的运行。     spawn oasswd [index $argv 1]     set password [index $argv 2]     expect "*password:"     send "$password "     expect "*password:"     send "$password "     expect eof   第一行以用户名做参数启动passwd程序,为方便起见,第二行把密码存到一个变量里面。和shell类似,变量的使用也 不需要提前声明。   在第三行,expect搜索模式"*password:",其中*允许匹配任意输入,所以对于避免指定所有细节而言是非常有效的。 上面的程序里没有action,所以expect检测到该模式后就继续运行。   一旦接收到提示后,下一行就就把密码送给当前进程。表明回车。(实际上,所有的C的关于字符的约定都支持)。上面 的程序中有两个expect-send序列,因为passwd为了对输入进行确认,要求进行两次输入。在非交互式程序里面,这是毫无 必要的,但由于假定passwd是在和用户进行交互,所以我们的脚本还是这样做了。   最后,"expect eof"这一行的作用是在passwd的输出中搜索文件结束符,这一行语句还展示了关键字的匹配。另外一 个关键字匹配就是timeout了,timeout被用于表示所有匹配的失败而和一段特定长度的时间相匹配。在这里eof是非常有必 要的,因为passwd被设计成会检查它的所有I/O是否都成功了,包括第二次输入密码时产生的最后一个新行。   这个脚本已经足够展示passwd命令的基本交互性。另外一个更加完备的例子回检查别的一些行为。比如说,下面的这 个脚本就能检查passwd程序的别的几个方面。所有的提示都进行了检查。对垃圾输入的检查也进行了适当的处理。进程死 亡,超乎寻常的慢响应,或者别的非预期的行为都进行了处理。     spawn passwd [index $argv 1]     expect     eof            {exit 1}              timeout            {exit 2}             "*No such user.*"    {exit 3}             "*New password:"         send "[index $argv 2 "     expect     eof            {exit 4}             timeout            {exit 2}             "*Password too long*"    {exit 5}             "*Password too short*"    {exit 5}             "*Retype ew password:"     send "[index $argv 3] "     expect     timeout            {exit 2}             "*Mismatch*"        {exit 6}             "*Password unchanged*"    {exit 7}             " "             expect    timeout            {exit 2}             "*"            {exit 6}             eof       这个脚本退出时用一个数字来表示所发生的情况。0表示passwd程序正常运行,1表示非预期的死亡,2表示锁定,等等。 使用数字是为了简单起见。expect返回字符串和返回数字是一样简单的,即使是派生程序自身产生的消息也是一样的。实际 上,典型的做法是把整个交互的过程存到一个文件里面,只有当程序的运行和预期一样的时候才把这个文件删除。否则这个 log被留待以后进一步的检查。   这个passwd检查脚本被设计成由别的脚本来驱动。这第二个脚本从一个文件里面读取参数和预期的结果。对于每一个输 入参数集,它调用第一个脚本并且把结果和预期的结果相比较。(因为这个任务是非交互的,一个普通的老式shell就可以用 来解释第二个脚本)。比如说,一个passwd的数据文件很有可能就象下面一样。     passwd.exp    3    bogus    -        -     passwd.exp    0    fred    abledabl    abledabl     passwd.exp    5    fred    abcdefghijklm    -     passwd.exp    5    fred    abc        -     passwd.exp    6    fred    foobar        bar         passwd.exp    4    fred    ^C        -   第一个域的名字是要被运行的回归脚本。第二个域是需要和结果相匹配的退出值。第三个域就是用户名。第四个域和 第五个域就是提示时应该输入的密码。减号仅仅表示那里有一个域,这个域其实绝对不会用到。在第一个行中,bogus表示 用户名是非法的,因此passwd会响应说:没有此用户。expect在退出时会返回3,3恰好就是第二个域。在最后一行中,^C就 是被切实的送给程序来验证程序是否恰当的退出。   通过这种方法,expect可以用来检验和调试交互式软件,这恰恰是IEEE的POSIX 1003.2(shell和工具)的一致性检验所 要求的。进一步的说明请参考Libes[6]。 7.[rogue 和伪终端]   Unix用户肯定对通过管道来和其他进程相联系的方式非常的熟悉(比如说:一个shell管道)。expect使用伪终端来和派 生的进程相联系。伪终端提供了终端语义以便程序认为他们正在和真正的终端进行I/O操作。   比如说,BSD的探险游戏rogue在生模式下运行,并假定在连接的另一端是一个可寻址的字符终端。可以用expect编程, 使得通过使用用户界面可以玩这个游戏。   rogue这个探险游戏首先提供给你一个有各种物理属性,比如说力量值,的角色。在大部分时间里,力量值都是16,但 在几乎每20次里面就会有一个力量值是18。很多的rogue玩家都知道这一点,但没有人愿意启动程序20次以获得一个好的配 置。下面的这个脚本就能达到这个目的。     for {} {1} {} {         spawn rogue         expect "*Str:18*"    break                 "*Str:16*"             close         wait     }     interact   第一行是个for循环,和C语言的控制格式很象。rogue启动后,expect就检查看力量值是18还是16,如果是16,程序 就通过执行close和wait来退出。这两个命令的作用分别是关闭和伪终端的连接和等待进程退出。rogue读到一个文件结束 符就推出,从而循环继续运行,产生一个新的rogue游戏来检查。   当一个值为18的配置找到后,控制就推出循环并跳到最后一行脚本。interact把控制转移给用户以便他们能够玩这个 特定的游戏。   想象一下这个脚本的运行。你所能真正看到的就是20或者30个初始的配置在不到一秒钟的时间里掠过屏幕,最后留给 你的就是一个有着很好配置的游戏。唯一比这更好的方法就是使用调试工具来玩游戏。   我们很有必要认识到这样一点:rogue是一个使用光标的图形游戏。expect程序员必须了解到:光标的运动并不一定 以一种直观的方式在屏幕上体现。幸运的是,在我们这个例子里,这不是一个问题。将来的对expect的改进可能会包括一 个内嵌的能支持字符图形区域的终端模拟器。 8.[ftp]   我们使用expect写第一个脚本并没有打印出"Hello,World"。实际上,它实现了一些更有用的功能。它能通过非交互 的方式来运行ftp。ftp是用来在支持TCP/IP的网络上进行文件传输的程序。除了一些简单的功能,一般的实现都要求用户 的参与。   下面这个脚本从一个主机上使用匿名ftp取下一个文件来。其中,主机名是第一个参数。文件名是第二个参数。         spawn    ftp    [index $argv 1]         expect "*Name*"         send     "anonymous "         expect "*Password:*"         send [exec whoami]         expect "*ok*ftp>*"         send "get [index $argv 2] "         expect "*ftp>*"   上面这个程序被设计成在后台进行ftp。虽然他们在底层使用和expect类似的机制,但他们的可编程能力留待改进。 因为expect提供了高级语言,你可以对它进行修改来满足你的特定需求。比如说,你可以加上以下功能:     :坚持--如果连接或者传输失败,你就可以每分钟或者每小时,甚         至可以根据其他因素,比如说用户的负载,来进行不定期的         重试。     :通知--传输时可以通过mail,write或者其他程序来通知你,甚至         可以通知失败。     :初始化-每一个用户都可以有自己的用高级语言编写的初始化文件         (比如说,.ftprc)。这和C shell对.cshrc的使用很类似。   expect还可以执行其他的更复杂的任务。比如说,他可以使用McGill大学的Archie系统。Archie是一个匿名的 Telnet服务,它提供对描述Internet上可通过匿名ftp获取的文件的数据库的访问。通过使用这个服务,脚本可以询问 Archie某个特定的文件的位置,并把它从ftp服务器上取下来。这个功能的实现只要求在上面那个脚本中加上几行就可 以。   现在还没有什么已知的后台-ftp能够实现上面的几项功能,能不要说所有的功能了。在expect里面,它的实现却 是非常的简单。"坚持"的实现只要求在expect脚本里面加上一个循环。"通知"的实现只要执行mail和write就可以 了。"初始化文件"的实现可以使用一个命令,source .ftprc,就可以了,在.ftprc里面可以有任何的expect命令。   虽然这些特征可以通过在已有的程序里面加上钩子函数就可以,但这也不能保证每一个人的要求都能得到满足。 唯一能够提供保证的方法就是提供一种通用的语言。一个很好的解决方法就是把Tcl自身融入到ftp和其他的程序中间去。 实际上,这本来就是Tcl的初衷。在还没有这样做之前,expect提供了一个能实现大部分功能但又不需要任何重写的方案。 9.[fsck]   fsck是另外一个缺乏足够的用户接口的例子。fsck几乎没有提供什么方法来预先的回答一些问题。你能做的就是给 所有的问题都回答"yes"或者都回答"no"。   下面的程序段展示了一个脚本如何的使的自动的对某些问题回答"yes",而对某些问题回答"no"。下面的这个脚本一 开始先派生fsck进程,然后对其中两种类型的问题回答"yes",而对其他的问题回答"no"。     for {} {1} {} {         expect             eof        break                     "*UNREF FILE*CLEAR?"    {send "r "}                 "*BAD INODE*FIX?"    {send "y "}                 "*?"            {send "n "}         }   在下面这个版本里面,两个问题的回答是不同的。而且,如果脚本遇到了什么它不能理解的东西,就会执行 interact命令把控制交给用户。用户的击键直接交给fsck处理。当执行完后,用户可以通过按"+"键来退出或者 把控制交还给expect。如果控制是交还给脚本了,脚本就会自动的控制进程的剩余部分的运行。     for {} {1} {}{         expect                          eof        break                     "*UNREF FILE*CLEAR?"    {send "y "}                 "*BAD INODE*FIX?"    {send "y "}                 "*?"            {interact +}         }   如果没有expect,fsck只有在牺牲一定功能的情况下才可以非交互式的运行。fsck几乎是不可编程的,但它 却是系统管理的最重要的工具。许多别的工具的用户接口也一样的不足。实际上,正是其中的一些程序的不足导 致了expect的诞生。 10.[控制多个进程:作业控制]   expect的作业控制概念精巧的避免了通常的实现困难。其中包括了两个问题:一个是expect如何处理经典的 作业控制,即当你在终端上按下^Z键时expect如何处理;另外一个就是expect是如何处理多进程的。   对第一个问题的处理是:忽略它。expect对经典的作业控制一无所知。比如说,你派生了一个程序并且发送 一个^Z给它,它就会停下来(这是伪终端的完美之处)而expect就会永远的等下去。   但是,实际上,这根本就不成一个问题。对于一个expect脚本,没有必要向进程发送^Z。也就是说,没有必 要停下一个进程来。expect仅仅是忽略了一个进程,而把自己的注意力转移到其他的地方。这就是expect的作业 控制思想,这个思想也一直工作的很好。   从用户的角度来看是象这样的:当一个进程通过spawn命令启动时,变量spawn_id就被设置成某进程的描述 符。由spawn_id描述的进程就被认为是当前进程。(这个描述符恰恰就是伪终端文件的描述符,虽然用户把它当 作一个不透明的物体)。expect和send命令仅仅和当前进程进行交互。所以,切换一个作业所需要做的仅仅是把 该进程的描述符赋给spawn_id。   这儿有一个例子向我们展示了如何通过作业控制来使两个chess进程进行交互。在派生完两个进程之后,一 个进程被通知先动一步。在下面的循环里面,每一步动作都送给另外一个进程。其中,read_move和write_move 两个过程留给读者来实现。(实际上,它们的实现非常的容易,但是,由于太长了所以没有包含在这里)。     spawn chess            ;# start player one     set id1    $spawn_id     expect "Chess "     send "first "            ;# force it to go first     read_move     spawn chess            ;# start player two     set id2    $spawn_id     expect "Chess "          for {} {1} {}{         send_move         read_move         set spawn_id    $id1                  send_move         read_move         set spawn_id    $id2     }    有一些应用程序和chess程序不太一样,在chess程序里,的两个玩家轮流动。下面这个脚本实现了一个冒 充程序。它能够控制一个终端以便用户能够登录和正常的工作。但是,一旦系统提示输入密码或者输入用户名的 时候,expect就开始把击键记下来,一直到用户按下回车键。这有效的收集了用户的密码和用户名,还避免了普 通的冒充程序的"Incorrect password-tryagain"。而且,如果用户连接到另外一个主机上,那些额外的登录也 会被记录下来。     spawn tip /dev/tty17        ;# open connection to     set tty $spawn_id        ;# tty to be spoofed     spawn login     set login $spawn_id     log_user 0          for {} {1} {} {         set ready [select $tty $login]                  case $login in $ready {             set spawn_id $login             expect                        {"*password*" "*login*"}{                   send_user $expect_match                   set log 1                  }                   "*"        ;# ignore everything else             set spawn_id    $             send $expect_match         }         case $tty in $ready {             set spawn_id    $tty             expect "* *"{                     if $log {                       send_user $expect_match                       set log 0                     }                    }                     "*" {                     send_user $expect_match                    }             set spawn_id     $             send $expect_match         }     }             这个脚本是这样工作的。首先连接到一个login进程和终端。缺省的,所有的对话都记录到标准输出上 (通过send_user)。因为我们对此并不感兴趣,所以,我们通过命令"log_user 0"来禁止这个功能。(有很多 的命令来控制可以看见或者可以记录的东西)。    在循环里面,select等待终端或者login进程上的动作,并且返回一个等待输入的spawn_id表。如果在 表里面找到了一个值的话,case就执行一个action。比如说,如果字符串"login"出现在login进程的输出中, 提示就会被记录到标准输出上,并且有一个标志被设置以便通知脚本开始记录用户的击键,直至用户按下了回 车键。无论收到什么,都会回显到终端上,一个相应的action会在脚本的终端那一部分执行。    这些例子显示了expect的作业控制方式。通过把自己插入到对话里面,expect可以在进程之间创建复杂 的I/O流。可以创建多扇出,复用扇入的,动态的数据相关的进程图。    相比之下,shell使得它自己一次一行的读取一个文件显的很困难。shell强迫用户按下控制键(比如, ^C,^Z)和关键字(比如fg和bg)来实现作业的切换。这些都无法从脚本里面利用。相似的是:以非交互方式运 行的shell并不处理"历史记录"和其他一些仅仅为交互式使用设计的特征。这也出现了和前面哪个passwd程 序的相似问题。相似的,也无法编写能够回归的测试shell的某些动作的shell脚本。结果导致shell的这些方 面无法进行彻底的测试。    如果使用expect的话,可以使用它的交互式的作业控制来驱动shell。一个派生的shell认为它是在交互 的运行着,所以会正常的处理作业控制。它不仅能够解决检验处理作业控制的shell和其他一些程序的问题。 还能够在必要的时候,让shell代替expect来处理作业。可以支持使用shell风格的作业控制来支持进程的运行。 这意味着:首先派生一个shell,然后把命令送给shell来启动进程。如果进程被挂起,比如说,发送了一个^Z, 进程就会停下来,并把控制返回给shell。对于expect而言,它还在处理同一个进程(原来那个shell)。   expect的解决方法不仅具有很大的灵活性,它还避免了重复已经存在于shell中的作业控制软件。通过使 用shell,由于你可以选择你想派生的shell,所以你可以根据需要获得作业控制权。而且,一旦你需要(比如 说检验的时候),你就可以驱动一个shell来让这个shell以为它正在交互式的运行。这一点对于在检测到它们 是否在交互式的运行之后会改变输出的缓冲的程序来说也是很重要的。   为了进一步的控制,在interact执行期间,expect把控制终端(是启动expect的那个终端,而不是伪终端) 设置成生模式以便字符能够正确的传送给派生的进程。当expect在没有执行interact的时候,终端处于熟模式 下,这时候作业控制就可以作用于expect本身。 11.[交互式的使用expect]   在前面,我们提到可以通过interact命令来交互式的使用脚本。基本上来说,interact命令提供了对对话 的自由访问,但我们需要一些更精细的控制。这一点,我们也可以使用expect来达到,因为expect从标准输入 中读取输入和从进程中读取输入一样的简单。 但是,我们要使用expect_user和send_user来进行标准I/O,同 时不改变spawn_id。   下面的这个脚本在一定的时间内从标准输入里面读取一行。这个脚本叫做timed_read,可以从csh里面调用, 比如说,set answer="timed_read 30"就能调用它。     #!/usr/local/bin/expect -f     set timeout [index $argv 1]     expect_user "* "     send_user $expect_match    第三行从用户那里接收任何以新行符结束的任何一行。最后一行把它返回给标准输出。如果在特定的时间 内没有得到任何键入,则返回也为空。    第一行支持"#!"的系统直接的启动脚本。(如果把脚本的属性加上可执行属性则不要在脚本前面加上expect)。 当然了脚本总是可以显式的用"expect scripot"来启动。在-c后面的选项在任何脚本语句执行前就被执行。比如 说,不要修改脚本本身,仅仅在命令行上加上-c "trace...",该脚本可以加上trace功能了(省略号表示trace的 选项)。    在命令行里实际上可以加上多个命令,只要中间以";"分开就可以了。比如说,下面这个命令行:     expect -c "set timeout 20;expect"    一旦你把超时时限设置好而且程序启动之后,expect就开始等待文件结束符或者20秒的超时时限。 如果遇 到了文件结束符(EOF),该程序就会停下来,然后expect返回。如果是遇到了超时的情况,expect就返回。在这两 中情况里面,都隐式的杀死了当前进程。    如果我们不使用expect而来实现以上两个例子的功能的话,我们还是可以学习到很多的东西的。在这两中情 况里面,通常的解决方案都是fork另一个睡眠的子进程并且用signal通知原来的shell。如果这个过程或者读先发 生的话,shell就会杀司那个睡眠的进程。 传递pid和防止后台进程产生启动信息是一个让除了高手级shell程序员 之外的人头痛的事情。提供一个通用的方法来象这样启动多个进程会使shell脚本非常的复杂。 所以几乎可以肯定 的是,程序员一般都用一个专门C程序来解决这样一个问题。    expect_user,send_user,send_error(向标准错误终端输出)在比较长的,用来把从进程来的复杂交互翻译成 简单交互的expect脚本里面使用的比较频繁。在参考[7]里面,Libs描述怎样用脚本来安全的包裹(wrap)adb,怎样 把系统管理员从需要掌握adb的细节里面解脱出来,同时大大的降低了由于错误的击键而导致的系统崩溃。    一个简单的例子能够让ftp自动的从一个私人的帐号里面取文件。在这种情况里,要求提供密码。 即使文件 的访问是受限的,你也应该避免把密码以明文的方式存储在文件里面。把密码作为脚本运行时的参数也是不合适的, 因为用ps命令能看到它们。有一个解决的方法就是在脚本运行的开始调用expect_user来让用户输入以后可能使用的 密码。这个密码必须只能让这个脚本知道,即使你是每个小时都要重试ftp。    即使信息是立即输入进去的,这个技巧也是非常有用。比如说,你可以写一个脚本,把你每一个主机上不 同的帐号上的密码都改掉,不管他们使用的是不是同一个密码数据库。如果你要手工达到这样一个功能的话,你必 须Telnet到每一个主机上,并且手工输入新的密码。而使用expect,你可以只输入密码一次而让脚本来做其它的事 情。    expect_user和interact也可以在一个脚本里面混合的使用。考虑一下在调试一个程序的循环时,经过好多 步之后才失败的情况。一个expect脚本可以驱动哪个调试器,设置好断点,执行该程序循环的若干步,然后将控制 返回给键盘。它也可以在返回控制之前,在循环体和条件测试之间来回的切换。 6.[passwd和一致性检查]   在前面,我们提到passwd程序在缺乏用户交互的情况下,不能运行,passwd 会忽略I/O重定向,也不能嵌入到管道里边以便能从别的程序或者文件里读取输 入。这个程序坚持要求真正的与用户进行交互。因为安全的原因,passwd被设计 成这样,但结果导致没有非交互式的方法来检验passwd。这样一个对系统安全 至关重要的程序竟然没有办法进行可靠的检验,真实具有讽刺意味。   passwd以一个用户名作为参数,交互式的提示输入密码。下面的expect脚 本以用户名和密码作为参数而非交互式的运行。     spawn oasswd [index $argv 1]     set password [index $argv 2]     expect "*password:"     send "$password "     expect "*password:"     send "$password "     expect eof   第一行以用户名做参数启动passwd程序,为方便起见,第二行把密码存到 一个变量里面。和shell类似,变量的使用也不需要提前声明。   在第三行,expect搜索模式"*password:",其中*允许匹配任意输入,所 以对于避免指定所有细节而言是非常有效的。 上面的程序里没有action,所以 expect检测到该模式后就继续运行。   一旦接收到提示后,下一行就就把密码送给当前进程。表明回车。(实 际上,所有的C的关于字符的约定都支持)。上面的程序中有两个expect-send 序列,因为passwd为了对输入进行确认,要求进行两次输入。在非交互式程序 里面,这是毫无必要的,但由于假定passwd是在和用户进行交互,所以我们的 脚本还是这样做了。   最后,"expect eof"这一行的作用是在passwd的输出中搜索文件结束符, 这一行语句还展示了关键字的匹配。另外一个关键字匹配就是timeout了, timeout被用于表示所有匹配的失败而和一段特定长度的时间相匹配。在这里 eof是非常有必要的,因为passwd被设计成会检查它的所有I/O是否都成功了, 包括第二次输入密码时产生的最后一个新行。   这个脚本已经足够展示passwd命令的基本交互性。另外一个更加完备的例 子回检查别的一些行为。比如说,下面的这个脚本就能检查passwd程序的别的 几个方面。所有的提示都进行了检查。对垃圾输入的检查也进行了适当的处 理。进程死亡,超乎寻常的慢响应,或者别的非预期的行为都进行了处理。     spawn passwd [index $argv 1]     expect     eof            {exit 1}              timeout            {exit 2}             "*No such user.*"    {exit 3}             "*New password:"         send "[index $argv 2 "     expect     eof            {exit 4}             timeout            {exit 2}             "*Password too long*"    {exit 5}             "*Password too short*"    {exit 5}             "*Retype ew password:"     send "[index $argv 3] "     expect     timeout            {exit 2}             "*Mismatch*"        {exit 6}             "*Password unchanged*"    {exit 7}             " "             expect    timeout            {exit 2}             "*"            {exit 6}             eof       这个脚本退出时用一个数字来表示所发生的情况。0表示passwd程序正常 运行,1表示非预期的死亡,2表示锁定,等等。使用数字是为了简单起见。 expect返回字符串和返回数字是一样简单的,即使是派生程序自身产生的消息 也是一样的。实际上,典型的做法是把整个交互的过程存到一个文件里面,只 有当程序的运行和预期一样的时候才把这个文件删除。否则这个log被留待以 后进一步的检查。   这个passwd检查脚本被设计成由别的脚本来驱动。这第二个脚本从一个文 件里面读取参数和预期的结果。对于每一个输入参数集,它调用第一个脚本并 且把结果和预期的结果相比较。(因为这个任务是非交互的,一个普通的老式 shell就可以用来解释第二个脚本)。比如说,一个passwd的数据文件很有可能 就象下面一样。     passwd.exp    3    bogus    -        -     passwd.exp    0    fred    abledabl    abledabl     passwd.exp    5    fred    abcdefghijklm    -     passwd.exp    5    fred    abc        -     passwd.exp    6    fred    foobar        bar         passwd.exp    4    fred    ^C        -   第一个域的名字是要被运行的回归脚本。第二个域是需要和结果相匹配的 退出值。第三个域就是用户名。第四个域和第五个域就是提示时应该输入的密 码。减号仅仅表示那里有一个域,这个域其实绝对不会用到。在第一个行中 ,bogus表示用户名是非法的,因此passwd会响应说:没有此用户。expect在 退出时会返回3,3恰好就是第二个域。在最后一行中,^C就是被切实的送给程 序来验证程序是否恰当的退出。   通过这种方法,expect可以用来检验和调试交互式软件,这恰恰是IEEE的 POSIX 1003.2(shell和工具)的一致性检验所要求的。进一步的说明请参考 Libes[6]。 7.[rogue 和伪终端]   Unix用户肯定对通过管道来和其他进程相联系的方式非常的熟悉(比如说: 一个shell管道)。expect使用伪终端来和派生的进程相联系。伪终端提供了终 端语义以便程序认为他们正在和真正的终端进行I/O操作。   比如说,BSD的探险游戏rogue在生模式下运行,并假定在连接的另一端是 一个可寻址的字符终端。可以用expect编程,使得通过使用用户界面可以玩这 个游戏。   rogue这个探险游戏首先提供给你一个有各种物理属性,比如说力量值,的 角色。在大部分时间里,力量值都是16,但在几乎每20次里面就会有一个力量 值是18。很多的rogue玩家都知道这一点,但没有人愿意启动程序20次以获得一 个好的配置。下面的这个脚本就能达到这个目的。     for {} {1} {} {         spawn rogue         expect "*Str:18*"    break                 "*Str:16*"             close         wait     }     interact   第一行是个for循环,和C语言的控制格式很象。rogue启动后,expect就 检查看力量值是18还是16,如果是16,程序就通过执行close和wait来退出。 这两个命令的作用分别是关闭和伪终端的连接和等待进程退出。rogue读到一 个文件结束符就推出,从而循环继续运行,产生一个新的rogue游戏来检查。   当一个值为18的配置找到后,控制就推出循环并跳到最后一行脚本。 interact把控制转移给用户以便他们能够玩这个特定的游戏。   想象一下这个脚本的运行。你所能真正看到的就是20或者30个初始的配置 在不到一秒钟的时间里掠过屏幕,最后留给你的就是一个有着很好配置的游戏 。唯一比这更好的方法就是使用调试工具来玩游戏。   我们很有必要认识到这样一点:rogue是一个使用光标的图形游戏。 expect程序员必须了解到:光标的运动并不一定以一种直观的方式在屏幕上体 现。幸运的是,在我们这个例子里,这不是一个问题。将来的对expect的改 进可能会包括一个内嵌的能支持字符图形区域的终端模拟器。 8.[ftp]   我们使用expect写第一个脚本并没有打印出"Hello,World"。实际上,它 实现了一些更有用的功能。它能通过非交互的方式来运行ftp。ftp是用来在支 持TCP/IP的网络上进行文件传输的程序。除了一些简单的功能,一般的实现都 要求用户的参与。   下面这个脚本从一个主机上使用匿名ftp取下一个文件来。其中,主机名 是第一个参数。文件名是第二个参数。         spawn    ftp    [index $argv 1]         expect "*Name*"         send     "anonymous "         expect "*Password:*"         send [exec whoami]         expect "*ok*ftp>*"         send "get [index $argv 2] "         expect "*ftp>*"   上面这个程序被设计成在后台进行ftp。虽然他们在底层使用和expect类 似的机制,但他们的可编程能力留待改进。因为expect提供了高级语言,你可 以对它进行修改来满足你的特定需求。比如说,你可以加上以下功能:     :坚持--如果连接或者传输失败,你就可以每分钟或者每小时,甚         至可以根据其他因素,比如说用户的负载,来进行不定期的         重试。     :通知--传输时可以通过mail,write或者其他程序来通知你,甚至         可以通知失败。     :初始化-每一个用户都可以有自己的用高级语言编写的初始化文件         (比如说,.ftprc)。这和C shell对.cshrc的使用很类似。   expect还可以执行其他的更复杂的任务。比如说,他可以使用McGill大学 的Archie系统。Archie是一个匿名的Telnet服务,它提供对描述Internet上可 通过匿名ftp获取的文件的数据库的访问。通过使用这个服务,脚本可以询问 Archie某个特定的文件的位置,并把它从ftp服务器上取下来。这个功能的实 现只要求在上面那个脚本中加上几行就可以。   现在还没有什么已知的后台-ftp能够实现上面的几项功能,能不要说所有 的功能了。在expect里面,它的实现却是非常的简单。"坚持"的实现只要求 在expect脚本里面加上一个循环。"通知"的实现只要执行mail和write就可以 了。"初始化文件"的实现可以使用一个命令,source .ftprc,就可以了, 在.ftprc里面可以有任何的expect命令。   虽然这些特征可以通过在已有的程序里面加上钩子函数就可以,但这也不 能保证每一个人的要求都能得到满足。唯一能够提供保证的方法就是提供一种 通用的语言。一个很好的解决方法就是把Tcl自身融入到ftp和其他的程序中间 去。实际上,这本来就是Tcl的初衷。在还没有这样做之前,expect提供了一 个能实现大部分功能但又不需要任何重写的方案。 9.[fsck]   fsck是另外一个缺乏足够的用户接口的例子。fsck几乎没有提供什么方法 来预先的回答一些问题。你能做的就是给所有的问题都回答"yes"或者都回答 "no"。   下面的程序段展示了一个脚本如何的使的自动的对某些问题回答"yes", 而对某些问题回答"no"。下面的这个脚本一开始先派生fsck进程,然后对其 中两种类型的问题回答"yes",而对其他的问题回答"no"。     for {} {1} {} {         expect             eof        break                     "*UNREF FILE*CLEAR?"    {send "r "}                 "*BAD INODE*FIX?"    {send "y "}                 "*?"            {send "n "}         }   在下面这个版本里面,两个问题的回答是不同的。而且,如果脚本遇到 了什么它不能理解的东西,就会执行interact命令把控制交给用户。用户的 击键直接交给fsck处理。当执行完后,用户可以通过按"+"键来退出或者把 控制交还给expect。如果控制是交还给脚本了,脚本就会自动的控制进程的 剩余部分的运行。     for {} {1} {}{         expect                          eof        break                     "*UNREF FILE*CLEAR?"    {send "y "}                 "*BAD INODE*FIX?"    {send "y "}                 "*?"            {interact +}         }   如果没有expect,fsck只有在牺牲一定功能的情况下才可以非交互式的 运行。fsck几乎是不可编程的,但它却是系统管理的最重要的工具。许多别 的工具的用户接口也一样的不足。实际上,正是其中的一些程序的不足导致 了expect的诞生。 10.[控制多个进程:作业控制]   expect的作业控制概念精巧的避免了通常的实现困难。其中包括了两个问 题:一个是expect如何处理经典的作业控制,即当你在终端上按下^Z键时 expect如何处理;另外一个就是expect是如何处理多进程的。   对第一个问题的处理是:忽略它。expect对经典的作业控制一无所知。比 如说,你派生了一个程序并且发送一个^Z给它,它就会停下来(这是伪终端的 完美之处)而expect就会永远的等下去。   但是,实际上,这根本就不成一个问题。对于一个expect脚本,没有必要 向进程发送^Z。也就是说,没有必要停下一个进程来。expect仅仅是忽略了 一个进程,而把自己的注意力转移到其他的地方。这就是expect的作业控制 思想,这个思想也一直工作的很好。   从用户的角度来看是象这样的:当一个进程通过spawn命令启动时,变量 spawn_id就被设置成某进程的描述符。由spawn_id描述的进程就被认为是当 前进程。(这个描述符恰恰就是伪终端文件的描述符,虽然用户把它当作一个 不透明的物体)。expect和send命令仅仅和当前进程进行交互。所以,切换一 个作业所需要做的仅仅是把该进程的描述符赋给spawn_id。   这儿有一个例子向我们展示了如何通过作业控制来使两个chess进程进行 交互。在派生完两个进程之后,一个进程被通知先动一步。在下面的循环里 面,每一步动作都送给另外一个进程。其中,read_move和write_move两个过 程留给读者来实现。(实际上,它们的实现非常的容易,但是,由于太长了所 以没有包含在这里)。     spawn chess            ;# start player one     set id1    $spawn_id     expect "Chess "     send "first "            ;# force it to go first     read_move     spawn chess            ;# start player two     set id2    $spawn_id     expect "Chess "          for {} {1} {}{         send_move         read_move         set spawn_id    $id1                  send_move         read_move         set spawn_id    $id2     }    有一些应用程序和chess程序不太一样,在chess程序里,的两个玩家 轮流动。下面这个脚本实现了一个冒充程序。它能够控制一个终端以便用户 能够登录和正常的工作。但是,一旦系统提示输入密码或者输入用户名的时 候,expect就开始把击键记下来,一直到用户按下回车键。这有效的收集了 用户的密码和用户名,还避免了普通的冒充程序的"Incorrect password-try again"。而且,如果用户连接到另外一个主机上,那些额外的登录也会被 记录下来。     spawn tip /dev/tty17        ;# open connection to     set tty $spawn_id        ;# tty to be spoofed     spawn login     set login $spawn_id     log_user 0          for {} {1} {} {         set ready [select $tty $login]                  case $login in $ready {             set spawn_id $login             expect                        {"*password*" "*login*"}{                   send_user $expect_match                   set log 1                  }                   "*"        ;# ignore everything else             set spawn_id    $             send $expect_match         }         case $tty in $ready {             set spawn_id    $tty             expect "* *"{                     if $log {                       send_user $expect_match                       set log 0                     }                    }                     "*" {                     send_user $expect_match                    }             set spawn_id     $             send $expect_match         }     }             这个脚本是这样工作的。首先连接到一个login进程和终端。缺省的, 所有的对话都记录到标准输出上(通过send_user)。因为我们对此并不感兴趣, 所以,我们通过命令"log_user 0"来禁止这个功能。(有很多的命令来控制 可以看见或者可以记录的东西)。    在循环里面,select等待终端或者login进程上的动作,并且返回一个 等待输入的spawn_id表。如果在表里面找到了一个值的话,case就执行一个 action。比如说,如果字符串"login"出现在login进程的输出中,提示就会 被记录到标准输出上,并且有一个标志被设置以便通知脚本开始记录用户的 击键,直至用户按下了回车键。无论收到什么,都会回显到终端上,一个相 应的action会在脚本的终端那一部分执行。    这些例子显示了expect的作业控制方式。通过把自己插入到对话里面, expect可以在进程之间创建复杂的I/O流。可以创建多扇出,复用扇入的, 动态的数据相关的进程图。    相比之下,shell使得它自己一次一行的读取一个文件显的很困难。 shell强迫用户按下控制键(比如,^C,^Z)和关键字(比如fg和bg)来实现作业的 切换。这些都无法从脚本里面利用。相似的是:以非交互方式运行的shell并 不处理"历史记录"和其他一些仅仅为交互式使用设计的特征。这也出现了和 前面哪个passwd程序的相似问题。相似的,也无法编写能够回归的测试shell 的某些动作的shell脚本。结果导致shell的这些方面无法进行彻底的测试。    如果使用expect的话,可以使用它的交互式的作业控制来驱动shell。一 个派生的shell认为它是在交互的运行着,所以会正常的处理作业控制。它不 仅能够解决检验处理作业控制的shell和其他一些程序的问题。还能够在必要 的时候,让shell代替expect来处理作业。可以支持使用shell风格的作业控 制来支持进程的运行。这意味着:首先派生一个shell,然后把命令送给shell 来启动进程。如果进程被挂起,比如说,发送了一个^Z,进程就会停下来,并 把控制返回给shell。对于expect而言,它还在处理同一个进程(原来那个 shell)。   expect的解决方法不仅具有很大的灵活性,它还避免了重复已经存在于 shell中的作业控制软件。通过使用shell,由于你可以选择你想派生的shell, 所以你可以根据需要获得作业控制权。而且,一旦你需要(比如说检验的时 候),你就可以驱动一个shell来让这个shell以为它正在交互式的运行。这一 点对于在检测到它们是否在交互式的运行之后会改变输出的缓冲的程序来说也 是很重要的。   为了进一步的控制,在interact执行期间,expect把控制终端(是启动 expect的那个终端,而不是伪终端)设置成生模式以便字符能够正确的传送给 派生的进程。当expect在没有执行interact的时候,终端处于熟模式下,这时 候作业控制就可以作用于expect本身。 11.[交互式的使用expect]   在前面,我们提到可以通过interact命令来交互式的使用脚本。基本上 来说,interact命令提供了对对话的自由访问,但我们需要一些更精细的控 制。这一点,我们也可以使用expect来达到,因为expect从标准输入中读取 输入和从进程中读取输入一样的简单。 但是,我们要使用expect_user和 send_user来进行标准I/O,同时不改变spawn_id。   下面的这个脚本在一定的时间内从标准输入里面读取一行。这个脚本叫 做timed_read,可以从csh里面调用,比如说,set answer="timed_read 30" 就能调用它。     #!/usr/local/bin/expect -f     set timeout [index $argv 1]     expect_user "* "     send_user $expect_match    第三行从用户那里接收任何以新行符结束的任何一行。最后一行把它 返回给标准输出。如果在特定的时间内没有得到任何键入,则返回也为空。    第一行支持"#!"的系统直接的启动脚本。(如果把脚本的属性加上可执 行属性则不要在脚本前面加上expect)。当然了脚本总是可以显式的用 "expect scripot"来启动。在-c后面的选项在任何脚本语句执行前就被执行。 比如说,不要修改脚本本身,仅仅在命令行上加上-c "trace...",该脚本可 以加上trace功能了(省略号表示trace的选项)。    在命令行里实际上可以加上多个命令,只要中间以";"分开就可以了。 比如说,下面这个命令行:     expect -c "set timeout 20;expect"    一旦你把超时时限设置好而且程序启动之后,expect就开始等待文件 结束符或者20秒的超时时限。 如果遇到了文件结束符(EOF),该程序就会停 下来,然后expect返回。如果是遇到了超时的情况,expect就返回。在这两 中情况里面,都隐式的杀死了当前进程。    如果我们不使用expect而来实现以上两个例子的功能的话,我们还是可 以学习到很多的东西的。在这两中情况里面,通常的解决方案都是fork另一个 睡眠的子进程并且用signal通知原来的shell。如果这个过程或者读先发生的 话,shell就会杀司那个睡眠的进程。 传递pid和防止后台进程产生启动信息 是一个让除了高手级shell程序员之外的人头痛的事情。提供一个通用的方法 来象这样启动多个进程会使shell脚本非常的复杂。 所以几乎可以肯定的是, 程序员一般都用一个专门C程序来解决这样一个问题。    expect_user,send_user,send_error(向标准错误终端输出)在比较长 的,用来把从进程来的复杂交互翻译成简单交互的expect脚本里面使用的比较 频繁。在参考[7]里面,Libs描述怎样用脚本来安全的包裹(wrap)adb,怎样 把系统管理员从需要掌握adb的细节里面解脱出来,同时大大的降低了由于错 误的击键而导致的系统崩溃。    一个简单的例子能够让ftp自动的从一个私人的帐号里面取文件。在这 种情况里,要求提供密码。 即使文件的访问是受限的,你也应该避免把密码 以明文的方式存储在文件里面。把密码作为脚本运行时的参数也是不合适的, 因为用ps命令能看到它们。有一个解决的方法就是在脚本运行的开始调用 expect_user来让用户输入以后可能使用的密码。这个密码必须只能让这个脚 本知道,即使你是每个小时都要重试ftp。    即使信息是立即输入进去的,这个技巧也是非常有用。比如说,你可 以写一个脚本,把你每一个主机上不同的帐号上的密码都改掉,不管他们使用 的是不是同一个密码数据库。如果你要手工达到这样一个功能的话,你必须 Telnet到每一个主机上,并且手工输入新的密码。而使用expect,你可以只输 入密码一次而让脚本来做其它的事情。    expect_user和interact也可以在一个脚本里面混合的使用。考虑一下 在调试一个程序的循环时,经过好多步之后才失败的情况。一个expect脚本 可以驱动哪个调试器,设置好断点,执行该程序循环的若干步,然后将控制 返回给键盘。它也可以在返回控制之前,在循环体和条件测试之间来回的切 换。转自:永远的UNIX expect下载:
阅读(894) | 评论(0) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。

我要回帖

更多关于 shell expect 的文章

 

随机推荐