Linux / Unix命令期望

预计是根据脚本与其他交互式程序进行交流的程序。 在脚本之后,Expect知道程序可以预期什么,以及正确的响应应该是什么。 解释型语言提供分支和高级控制结构来指导对话。 另外,用户可以在需要时直接控制和交互,然后将控制权返回给脚本。

Expectk是Expect和Tk的混合体。 它的行为就像Expect和Tk的愿望。 没有Tcl,Expect也可以直接在C或C ++中使用。

“Expect”这个名字来自uucp,kermit和其他调制解调器控制程序所普及的send / expect序列的思想。 然而,与uucp不同,Expect是泛化的,因此它可以作为任何程序和任务的用户级命令运行。 期望可以同时与几个程序交谈。

什么期望可以做

例如,以下是expect命令可以执行的一些操作:

有多种原因导致shell无法执行这些任务。 所有可能与预期。

通常,Expect对于运行需要程序和用户之间交互的任何程序很有用。 所有这一切都是必要的,可以通过编程来表征交互。 期望也可以在不停止正在控制的程序的情况下对用户进行控制。 同样,用户可以随时将控制权返回给脚本。

用法

Expect读取cmdfile以获取要执行的命令列表。 可以在支持#!的系统上隐式调用期望值。 通过将脚本标记为可执行文件并在脚本中创建第一行:

#!/ usr / local / bin / expect -f

当然,路径必须准确地描述期望生活的地方。 / usr / local / bin只是一个例子。

-c标志表示要在脚本中的任何脚本之前执行的命令。 应引用该命令以防止被shell破坏。 该选项可能会多次使用。 多个命令可以用一个-c通过用分号分隔来执行。 命令按它们出现的顺序执行。 使用Expectk时,此选项被指定为-command。

-d标志启用一些诊断输出,主要报告诸如期望和交互等命令的内部活动。 此标志与Expect脚本开头处的“exp_internal 1”具有相同的效果,并且还会打印Expect版本。

-D标志启用交互式调试器。 应该有一个整数值。 如果该值不为零,或者如果按下^ C或命中断点,或者其他适当的调试器命令出现在脚本中,则调试器将在下一个Tcl过程之前进行控制。 使用Expectk时,该选项被指定为- Debug。

-f标志表示一个文件,从中读取命令。 标志本身是可选的,因为它仅在使用#时才​​有用! 符号,以便可以在命令行上提供其他参数。 当使用Expectk时,这个选项被指定为-file。

默认情况下,命令文件被读入内存并执行完整。 偶尔需要一次读取一行文件。 为了强制以这种方式处理任意文件,请使用-b标志。 当使用Expectk时,这个选项被指定为-buffer。

如果字符串“ - ”作为文件名提供,则读取标准输入。 使用“./-”从实际名为“ - ”的文件读取。

-i标志使Expect交互式地提示输入命令,而不是从文件中读取它们。 通过退出命令或EOF终止提示。 如果既不使用命令文件也不使用-c,则假定-i标志。 使用Expectk时,此选项被指定为-interactive。

- 可以用来界定选项的结尾。 如果您希望在没有Expect解释的情况下将类似选项的参数传递给脚本,这非常有用。 这可以有用地放在#! 以防止预期的任何旗帜式解释。 例如,以下内容会将原始参数(包括脚本名称)保留在变量argv中

#!/ usr / local / bin / expect -

请注意,向#!添加参数时必须遵守通常的getopt (3)和execve(2)约定。 线。

如果存在文件$ exp_library / expect.rc,则会自动采购,除非使用-N标志。 (使用Expectk时,这个选项被指定为-NORC。)在此之后,除非使用了-n标志,否则文件〜/ .expect.rc会自动采集。 如果定义了环境变量DOTDIR,则将其视为目录,并从中读取.expect.rc。 使用Expectk时,此选项指定为-norc。 只有在执行任何-c标志后才会发生此采购。

-v导致期望打印其版本号并退出。 Expectk中使用长标志名称的相应标志是-version。

可选参数被构造成一个列表并存储在名为argv和。 argc被初始化为argv的长度。

如果不使用脚本,Argv0被定义为脚本或二进制文件的名称。 例如,以下输出脚本的名称和前三个参数:

send_user“$ argv0 [lrange $ argv 0 2] \ n”

命令

Expect使用工具命令语言。 Tcl提供了控制流(如果需要中断),表达式评估以及其他一些特性,如递归和过程定义。 这里使用但未定义的命令(set,if,exec)是Tcl命令。 Expect支持其他命令。 除非另外指定,否则命令将返回空字符串。

命令按字母顺序列出,以便它们可以快速定位。 但是,新用户可能会发现通过阅读spawn,发送,期望和交互的顺序来更容易开始。

关闭[-slave] [-onexec 0 | 1] [-i spawn_id]

关闭与当前进程的连接。 大多数交互式程序会在他们的stdin上检测EOF并退出; 因此关闭通常足以杀死进程 。 -i标志声明进程关闭对应于命名的spawn_id。

期望和交互都会检测当前进程何时退出并隐式执行结束,但如果通过例如“exec kill $ pid”来终止进程 ,则需要明确地调用close

-onexec标志确定是否在任何新产生的进程中关闭了spawn id,或者是否覆盖了进程 。 要使spawn id保持打开状态,请使用值0.非零整数值将强制在任何新进程中关闭spawn。

-slave标志关闭与spawn id关联的从机。 当连接关闭时,如果奴隶仍然打开,奴隶也会自动关闭。

不管连接是隐式关闭还是显式关闭,都应该调用wait来清除相应的内核进程槽。 close命令不会调用wait,因为不能保证关闭进程连接会导致它退出。

调试[[--now] 0 | 1]

控制一个Tcl调试器,允许您逐句执行语句并设置断点。

没有参数时,如果调试器没有运行,则返回1,否则返回0。

有了1个参数,调试器就会启动。 使用0参数时,调试器停止。 如果1参数前面有-now标志,则立即启动调试器。 否则,调试器将以下一个Tcl语句启动。

调试命令不会更改任何陷阱。 使用-D标志将此与启动Expect进行比较。

disconnect命令断开终端的分叉进程 。 它继续在后台运行。 该过程有其自己的过程组。 标准I / O被重定向到/ dev / null

以下片段使用断开连接来继续在后台运行脚本。

如果{[fork]!= 0}退出断开连接。 。 。

以下脚本读取密码,然后每小时运行一个程序,每次运行时都要求输入密码。 脚本提供密码,以便您只需输入一次即可。

send_user“password?\”expect_user -re“(。*)\ n”{} 1 {} {if {[fork]!= 0} {sleep 3600; continue} disconnect spawn priv_prog expect Password:send“$ expect_out 1,字符串)\ r“。 。 。 出口 }

在外壳异步过程特征(&)上使用断开连接的优点是,Expect可以在断开连接之前保存终端参数,然后将它们应用于新的ptys。 使用&时,Expect没有机会读取终端的参数,因为终端在Expect接收控制时已断开连接。

退出[-opts] [状态]

导致期望退出或准备这样做。

-onexit标志导致下一个参数被用作退出处理程序。 没有参数,返回当前的退出处理程序。

-noexit标志导致Expect准备退出,但是没有实际将控制权返回给操作系统。 用户定义的退出处理程序与Expect自己的内部处理程序一样运行。 不应再执行其他期望的命令。 如果您使用其他Tcl扩展运行Expect,这很有用。 当前的解释器(和主窗口,如果在Tk环境中)仍然保持其他Tcl扩展可以清理。 如果Expect的退出再次被调用(但是可能发生这种情况),则处理程序不会重新运行。

退出后,所有关于产生的进程的连接都将关闭。 生成的进程会将封闭检测为EOF。 退出除了正常的_exit(2)过程所做的操作之外不会采取其他操作。 因此,不会检查EOF的衍生进程可能会继续运行。 (例如,各种条件对于确定产生的进程将发送什么信号很重要,但这些是依赖于系统的,通常在退出(3)中进行记录。)继续运行的衍生进程将由init继承。

状态 (如果未指定,则为0)作为Expect的退出状态返回。 如果到达脚本的末尾,则退出将隐式执行。

exp_continue [-continue_timer]
命令exp_continue允许期望自己继续执行而不是像往常那样返回。 默认情况下, exp_continue重置超时定时器。 -continue_timer标志防止重启计时器。 (请看更多信息。)

exp_internal [-f文件]值
如果不为零,则会导致进一步的命令将内部诊断信息发送到期望值 stderr 。 如果值为 0,则此输出被禁用。诊断信息包括接收到的每个字符,并且每次尝试都将当前输出与模式匹配。

如果提供了可选文件 ,则将所有正常和调试输出写入该文件(不管值的 )。 以前的任何诊断输出文件都已关闭。

-info标志使exp_internal返回给定的最近的非信息参数的描述。

exp_open [args] [-i spawn_id]
返回一个对应于原始spawn id的Tcl文件标识符。 文件标识符可以像使用Tcl的打开命令打开一样使用 。 (spawn id不应该再使用,不应该执行等待

-leaveopen标志通过Expect命令将spawn id打开以供访问。 必须在spawn id上执行等待

exp_pid [-i spawn_id]
返回当前派生进程对应的进程ID。 如果使用-i标志,则返回的pid对应于给定spawn id的pid

exp_send
发送的别名。

exp_send_error
send_error的别名。

exp_send_log
send_log的别名。

exp_send_tty
send_tty的别名。

exp_send_user
send_user的别名。

exp_version [[-exit]版本]
对于确保脚本与当前版本的Expect兼容很有用。

没有参数,返回当前版本的Expect 。 这个版本可能会被编码到您的脚本中。 如果您确实知道您未使用最新版本的功能,则可以指定较早的版本。

版本由三个由点分隔的数字组成。 首先是主要数字。 为不同版本的Expect编写的脚本几乎肯定不起作用。 如果主要数字不匹配,则exp_version将返回错误。

其次是次要号码。 编写比当前版本号码更小的版本的脚本可能取决于某些新功能,可能无法运行。 如果主要编号匹配,则exp_version将返回错误,但脚本次要编号大于正在运行的Expect的编号

第三是在版本比较中没有参与的数字。 但是,当Expect软件分发以任何方式进行更改(例如通过附加文档或优化)时,它会增加。 在每个新的次要版本中,它重置为0。

使用-exit标志, Expect会打印一个错误并在版本过期时退出。

期望[[-opts] pat1 body1] ... [-opts] patn [bodyn]
等待其中一个模式与衍生进程的输出相匹配,指定的时间段已过,或者看到文件结束。 如果最后的主体是空的,它可能被省略。

最新的expect_before命令中的模式在任何其他模式之前隐式使用。 来自最近的expect_after命令的模式在任何其他模式之后隐式使用。

如果整个expect语句的参数需要多行,则所有参数都可以“加强”为一个,以避免每行都以反斜杠结尾。 在这种情况下,尽管有大括号,通常的Tcl替换仍会发生。

如果模式是关键字eof ,则在文件结束时执行相应的主体。 如果模式是关键字超时 ,则在超时时执行相应的主体。 如果没有使用超时关键字,则会在超时后执行隐式空操作。 默认的超时时间是10秒,但可以通过命令“set timeout 30”将其设置为30,例如30。 无限超时可以由值-1指定。 如果模式是关键字default ,则在超时或文件结束时执行相应的主体。

如果模式匹配,则执行相应的主体。 expect返回body的结果(如果没有模式匹配,则返回空字符串)。 在多个模式匹配的情况下,首先出现的模式用于选择主体。

每次新输出到达时,都会按照它们列出的顺序与每个模式进行比较。 因此,您可以通过确保最后一个模式出现,如提示来测试是否存在匹配。 在没有提示的情况下,您必须使用超时 (就像您手动进行交互一样)。

模式有三种规定。 默认情况下,模式与Tcl的字符串匹配命令一样被指定。 (这些模式也类似于通常称为“glob”模式的C-shell正则表达式)。 -gl标志可以用于保护可能与期望标志匹配的模式。 任何以“ - ”开头的模式都应该这样保护。 (所有以“ - ”开头的字符串都保留给将来的选项。)

例如,以下片段查找成功的登录名。 (请注意, abort被假定为脚本中其他地方定义的一个程序。)

expect {busy {puts busy \ n; exp_continue}失败中止“无效密码”中止超时中止连接}

引号对于第四种模式是必要的,因为它包含一个空格,否则会将模式与动作分开。 具有相同动作的模式(例如第3和第4)需要再次列出动作。 这可以通过使用正则表达式样式来避免(见下文)。 有关形成全局模式的更多信息可以在Tcl手册中找到。

正则表达式样式遵循Tcl的正则表达式 (简称“正则表达式”)命令定义的语法。 正则表达式模式与标志-re一起引入。 前面的例子可以用regexp重写为:

expect {busy {puts busy \ n; exp_continue} -re“失败|无效密码”中止超时中止连接}

这两种模式都是“未固定的”。 这意味着模式不必匹配整个字符串,但可以在字符串中的任何位置开始和结束匹配(只要其他所有匹配项匹配)。 使用^匹配字符串的开头,使用$匹配结尾。 请注意,如果您不等待字符串结束,那么您的响应可能会很容易在字符串中间结束,因为它们是从衍生进程中回显的。 在产生正确结果的同时,输出看起来不自然。 因此,如果您可以准确地描述字符串末尾的字符,则鼓励使用$。

请注意,在许多编辑器中,^和$分别与行的开头和结尾相匹配。 但是,因为期望不是面向行的,所以这些字符匹配当前在期望匹配缓冲区中的数据的开始和结束(而不是行)。 (另请参阅下面有关“系统消化不良”的注释。)

-ex标志使模式匹配为“精确”字符串。 没有对*,^等的解释(尽管通常的Tcl约定仍然必须遵守)。 确切的模式总是未固定的。

-nocase标志会导致输出的大写字符比较,就好像它们是小写字符一样。 该模式不受影响。

在读取输出时,超过2000 个字节可能会迫使较早的字节“被遗忘”。 这可以通过函数match_max来改变。 (请注意,过大的值可能会减慢模式匹配。)如果patlistfull_buffer ,则在接收到match_max字节并且没有其他模式匹配的情况下执行相应的主体。 不管是否使用full_buffer关键字,忘记的字符都会写入expect_out(buffer)。

如果patlist是关键字null ,并且允许NULL(通过remove_nulls命令),则在单个ASCII 0匹配时执行相应的主体。 通过glob或regexp模式不可能匹配0个字节。

在匹配一个模式(或者eof或者full_buffer)之后,任何匹配和以前不匹配的输出将被保存在变量expect_out(buffer)中 。 通过expect_out(9,string)将多达9个正则表达式子字符串匹配保存在变量expect_out(1 ,字符串)中 。 如果在模式之前使用-indices标志,则10个字符串的开始和结束索引(以适合于lrange的形式)存储在变量expect_out(X,start)expect_out(X,end)中 ,其中X是数字,对应于缓冲区中的子串位置。 0指的是匹配整个模式的字符串,并为glob模式和正则表达式模式生成。 例如,如果某个进程已生成“abcdefgh \ n”的输出,则结果为:

期待“cd”

就好像下面的语句已经执行一样:

set expect_out(0,string)cd set expect_out(buffer)abcd

并且“efgh \ n”保留在输出缓冲区中。 如果一个过程产生了输出“abbbcabkkkka \ n”,结果如下:

expect -indices -re“b(b *)。*(k +)”

就好像下面的语句已经执行一样:

set expect_out(0,start)1 set expect_out(0,end)10 set expect_out(0,string)bbbcabkkkk set expect_out(1,start)2 set expect_out(1,end)3 set expect_out(1,string)bb set expect_out (2,start)10 set expect_out(2,end)10 set expect_out(2,string)k set expect_out(buffer)abbbcabkkkk

并且“a \ n”保留在输出缓冲区中。 模式“*”(和-re“。*”)将刷新输出缓冲区而不读取进程的任何更多输出。

通常,匹配的输出将从Expect的内部缓冲区中丢弃。 这可以通过在-notransfer标志前加一个模式来防止。 这个标志在实验中特别有用(为了方便,在试验时可以缩写为“-not”)。

与匹配输出(或eof或full_buffer)关联的spawn id存储在expect_out(spawn_id)中

-timeout标志导致当前的expect命令将下面的值用作超时而不是使用超时变量的值。

默认情况下,模式与当前进程的输出匹配,但-i标志声明命名spawn_id列表的输出与任何后续模式匹配(直到下一个-i )。 spawn_id列表应该是Spawn_ids的空白分隔列表或引用这样的spawn_ids列表的变量。

例如,以下示例等待来自当前进程的“连接”,或等待$ proc2命名的spawn_id中的“busy”,“failed”或“invalid password”。

预计{-i $ proc2 busy {puts busy \ n; exp_continue} -re“失败|无效密码”中止超时中止连接}

全局变量any_spawn_id的值可用于将模式与当前expect命令中所有其他-i标志命名的spawn_id进行匹配。 来自没有关联模式的-i标志的spawn_id(即紧接着另一个-i )可用于与any_spawn_id相关的相同expect命令中的任何其他模式

-i标志也可以命名全局变量,在这种情况下,读取变量以获得spawn id列表。 只要变化发生,变量就会被重读。 这提供了一种在命令执行时更改I / O源的方法。 提供这种方式的产卵ids被称为“间接”产卵ids。

诸如breakcontinue之类的操作会导致控制结构(即forproc )以通常的方式运行。 命令exp_continue允许期望自己继续执行而不是像往常那样返回。

这对避免显式循环或重复期望语句很有用。 以下示例是rlogin自动化片段的一部分。 exp_continue避免了如果rlogin提示输入密码,则必须编写第二个expect语句(再次查找提示)。

在$ host:“expect_user -re”(。*)\ n“send_user”\ n“send”$ expect_out(1,字符串)\ r“stty中预期{密码:{stty -echo send_user” echo exp_continue}不正确{send_user“无效密码或帐户\ n”退出}超时{send_user“连接到$主机超时\ n”退出} eof {send_user \“连接到主机失败:$ expect_out(buffer)”exit} - 重新$提示}

例如,以下片段可能有助于用户指导已经完全自动化的交互。 在这种情况下,终端进入原始模式。 如果用户按下“+”,则会增加一个变量。 如果按下“p”,则会向该进程发送多个返回值,可能以某种方式戳它,而“i”则让用户与进程交互,从而有效地从脚本中窃取控制权。 在每种情况下, exp_continue都允许当前期望在执行当前操作后继续进行模式匹配。

stty raw -echo expect_after {-i $ user_spawn_id“p”{send“\ r \ r \ r”; exp_continue}“+”{incr foo; exp_continue}“i”{interact; exp_continue}“quit”exit}

默认情况下, exp_continue重置超时定时器。 如果使用-continue_timer标志调用exp_continue ,定时器不会重新启动。

expect_after [expect_args]
expect_before的工作方式相同,只是如果来自expectexpect_after的模式可以匹配,则使用预期模式。 有关更多信息,请参阅expect_before命令。

expect_background [expect_args]
采用与期望相同的参数,但是它立即返回。 每当新输入到达时都会测试模式。 模式超时默认expect_background没有意义,并且被默默丢弃。 否则, expect_background命令就像expect一样使用expect_beforeexpect_after模式。

expect_background操作正在评估时,同一spawn id的后台处理被阻止。 后台处理在操作完成时解锁。 当后台处理被阻止时,可以对相同的spawn id进行(前台) 期望

expect_background未被阻止的情况下执行预期不可能的。 通过使用相同的spawn id声明新的expect_background来删除特定spawn id的expect_background。 在没有模式的情况下声明expect_background会将匹配的spawn id从匹配背景中的模式的能力中移除。

expect_before [expect_args]
采用与期望相同的参数,但是它立即返回。 具有相同spawn id的最近expect_before中的模式动作对隐式添加到以下任何expect命令中。 如果一个模式匹配,它将被视为在expect命令本身中指定的,并且关联主体在expect命令的上下文中执行。 如果来自expect_beforeexpect的模式可以匹配,则使用expect_before模式。

如果未指定模式,则不检查派生标识以查找任何模式。

除非由-i标志覆盖,否则expect_before模式与在执行expect_before命令时定义的spawn id相匹配(而不是在其模式匹配时)。

-info标志会导致expect_before返回它将匹配的模式的当前规范。 默认情况下,它会报告当前的spawn id。 可以给出一个可选的spawn id规范来获取该spawn id的信息。 例如

expect_before -info -i $ proc

最多可以给出一个spawn id规范。 标志-indirect抑制直接来自间接规范的spawn id。

而不是spawn id规范,标志“-all”将导致“-info”报告所有生成ids。

-info标志的输出可以重新用作expect_before的参数。

expect_tty [expect_args]
就像期待的一样,但它读取/ dev / tty中的字符(即用户的击键)。 默认情况下,阅读是在烹饪模式下进行的。 因此,线路必须以回报结束,以期望看到它们。 这可以通过stty改变(参见下面的stty命令)。

expect_user [expect_args]
就像期望的一样,但是它从标准输入读取字符(即用户的击键)。 默认情况下,阅读是在烹饪模式下进行的。 因此,线路必须以回报结束,以期望看到它们。 这可以通过stty改变(参见下面的stty命令)。

叉子
创建一个新的过程 。 新进程是当前Expect 进程的精确副本。 成功时, fork将返回0给新(子) 进程 ,并将子进程进程 ID返回给父进程 。 在失败时(总是由于缺乏资源,例如交换空间,内存), fork向父进程返回-1,并且没有创建子进程

通过退出命令退出分叉进程,就像原始进程一样 。 分叉的进程被允许写入日志文件。 如果您不禁用大多数进程的调试或日志记录,结果可能会造成混淆。

一些pty实现可能会被多个读者和作者混淆,即使是暂时的。 因此,在产卵过程之前分叉是最安全的。

交互[string1 body1] ... [stringn [bodyn]]
将当前进程的控制权交给用户,以便将键击发送到当前进程 ,并返回当前进程的stdout和stderr。

字符串主体对可以被指定为参数,在这种情况下,当输入相应的字符串时执行主体。 (默认情况下,字符串不会发送到当前进程 。)假定解释器命令(如果最后一个主体缺失)。

如果整个交互语句的参数需要多行,则所有参数都可以“加强”为一行,以避免每行都以反斜杠结尾。 在这种情况下,尽管有大括号,通常的Tcl替换仍会发生。

例如,以下命令运行时会与定义的以下字符串 - 主体对进行交互:按^ Z时, Expect被挂起。 ( -reset标志恢复终端模式。)当^ A被按下时,用户看到“你输入了一个控制-A”并且该过程被发送^ A。 当按下$时,用户看到日期。 按^ C时, Expect退出。 如果输入“foo”,用户将看到“bar”。 当按下~~时, Expect解释器以交互方式运行。

设置CTRLZ \ 032 interact {-reset $ CTRLZ {exec kill -STOP [pid]} \ 001 {send_user“你键入了一个控制-A \ n”; 发送“\ 001”} $ {send_user“日期是[clock format [clock seconds]]。”} \ 003 exit foo {send_user“bar”} ~~}

在字符串正文对中,字符串按照它们作为参数列出的顺序进行匹配。 部分匹配的字符串不会被发送到当前进程,以预期剩余字符串。 如果字符被输入以致不再有可能匹配,则只有字符串的一部分将被发送到不可能开始另一匹配的进程 。 因此,如果尝试匹配的原始字符串最终失败,那么作为部分匹配子字符串的字符串可以稍后匹配。

默认情况下,字符串匹配确切无法使用通配符 。 (相比之下, expect命令默认使用全局样式模式。)- ex标志可用于保护可能与交互标志匹配的模式。 任何以“ - ”开头的模式都应该这样保护。 (所有以“ - ”开头的字符串都保留给将来的选项。)

-re标志强制字符串被解释为正则表达式样式。 在这种情况下,匹配的子字符串存储在变量interact_out中,类似于expect将其输出存储在变量expect_out中的方式-indices标志同样受支持。

模式eof引入了一个在文件结束时执行的动作。 一个单独的eof模式也可能跟在-output标志后面,在这种情况下,如果在写入输出时检测到eof,它将与之匹配。 默认的eof动作是“return”,因此交互只是返回任何EOF。

模式超时引入超时(以秒为单位)以及在给定时间内未读取任何字符后执行的操作。 超时模式适用于最近指定的进程 。 没有默认超时。 特殊变量“timeout”(由expect命令使用)对此超时没有影响。

例如,以下语句可用于自动登出一小时内没有键入任何内容但仍有频繁系统消息的用户:

交互-input $ user_spawn_id超时3600返回-output \ $ spawn_id

如果模式是关键字null ,并且允许空值(通过remove_nulls命令),则在单个ASCII 0匹配时执行相应的主体。 通过glob或regexp模式不可能匹配0个字节。

使用-iwrite标志预填模式会导致变量interact_out(spawn_id)被设置为与模式(或eof)匹配的spawn_id。

诸如breakcontinue之类的操作会导致控制结构(即forproc )以通常的方式运行。 然而返回原因交互返回到它的调用者,而inter_return导致交互导致其调用者返回。 例如,如果“proc foo”调用交互 ,然后执行inter_return动作, proc foo将返回。 (这意味着如果交互式交互调用解释器 ,则返回将导致交互继续,而inter_return将导致交互返回到其调用者。)

交互过程中 ,使用原始模式,以便所有字符都可以传递给当前进程 。 如果当前进程没有捕获到作业控制信号,则在发送停止信号时(默认为^ Z),它将停止。 要重新启动它,请发送一个继续信号(例如通过“kill -CONT”)。 如果你真的想发送一个SIGSTOP到这样的进程 (^ Z),考虑先产生csh,然后运行你的程序。 另一方面,如果你想发送一个SIGSTOP到Expect本身,首先调用解释器(也许使用一个转义字符),然后按^ Z。

字符串主体对可以用作避免必须以交互方式进入解释器和执行命令的简写。 前一个终端模式在执行字符串主体对的主体时使用。

为了提高速度,默认情况下,操作以原始模式执行 -reset标志将终端重置为它执行交互之前的模式(总是为熟化模式)。 请注意,切换模式时输入的字符可能会丢失(某些系统中终端驱动程序的不幸功能)。 使用-reset的唯一原因是如果您的操作取决于在熟化模式下运行。

-echo标志将与以下模式相匹配的字符发回到在读取每个字符时生成它们的进程 。 当用户需要查看部分类型模式的反馈时,这可能很有用。

如果一个模式被回显,但最终无法匹配,这些字符将被发送到派生进程 。 如果产生的过程回应它们,用户将看到两个字符。 -echo可能仅适用于用户不太可能无法完成该模式的情况。 例如,以下摘录来自rftp,即recursive-ftp脚本,其中提示用户输入〜g,〜p或〜l以递归地获取,放入或列出当前目录。 这些远离正常的ftp命令,用户不太可能输入〜其次,除了错误之外,在这种情况下,他们可能会忽略结果。

交互{-echo〜g {getcurdirectory 1} -echo〜l {getcurdirectory 0} -echo〜p {putcurdirectory}}

当字符被读取时, -nobuffer标志将与以下模式匹配的字符发送到输出进程

当你想让程序回显模式时,这很有用。 例如,以下内容可能用于监视某人正在拨打的位置(海耶斯式调制解调器)。 每次看到“atd”脚本都会记录该行的其余部分。

proc lognumber {} {interact -nobuffer -re“(。*)\ r”return puts $ log“[clock format [clock seconds]]:dialed $ interact_out(1,string)”} interact -nobuffer“atd”lognumber

交互期间,以前使用log_user将被忽略。 特别是, 交互会迫使其输出被记录(发送到标准输出),因为它被假定为用户不希望盲目交互。

-o标志将导致任何后续的键身对应用于当前进程的输出。 例如,在处理在Telnet会话期间发送不需要的字符的主机时,这可能很有用。

默认情况下, 交互期望用户写入stdin并读取Expect 进程本身的stdout。 -u标志(用于“用户”)使得交互将用户看作由其参数命名的进程 (其必须是衍生的id)。

这允许两个不相关的进程在不使用显式循环的情况下连接在一起。 为了帮助调试,Expect的诊断程序总是转到stderr(或者stdout用于某些日志记录和调试信息)。 出于同样的原因, 解释器命令将从stdin交互式读取。

例如,下面的片段创建一个登录过程 。 然后拨打用户(未显示),最后连接两者。 当然,任何进程都可以替代登录。 例如,一个外壳将允许用户在不提供帐户和密码的情况下工作。

产卵登录设置登录$ spawn_id spawn提示调制解调器#拨回给用户#连接用户登录相互作用-u $登录

要将输出发送到多个进程,请列出以-output标志开头的每个spawn id列表。 一输出spawn id的输入可以由一个以-input标志开头的spawn id列表确定。 (除了any_spawn_id在交互中没有意义外,-input和-output都可以采用与expect命令中的-i标志形式相同的列表。)以下所有标志和字符串 (或模式)均适用于该输入,直到另一个 -输入标志出现。 如果没有输入, 输出意味着“-input $ user_spawn_id -output”。 (同样,对于没有输入的模式)。如果指定了一个输入,它将覆盖$ user_spawn_id。 如果指定了第二个输入,它将覆盖$ spawn_id。 可以指定附加的输入标志。

两个隐含的输入过程默认将其输出指定为$ spawn_id和$ user_spawn_id(反向)。 如果出现的输入标志没有输出标志,则该进程的字符将被丢弃。

当没有使用其他输入或输出标志时, -i标志引入了对当前spawn_id的替换。 -i标志意味着-o标志。

通过使用间接spawn id可以更改正在与之交互的进程。 (关于expect命令的部分介绍了间接spawn id。)可以使用-i,-u,-input或-output标志指定间接spawn id。

口译员[args]
使交互式提示用户输入Expect和Tcl命令。 每个命令的结果都被打印出来。

诸如breakcontinue之类的操作会导致控制结构(即forproc )以通常的方式运行。 然而, 返回会导致解释器返回到调用者,而inter_return会导致解释器在其调用者中返回。 例如,如果“proc foo”调用解释器然后执行inter_return动作, proc foo将返回。 任何其他命令都会导致解释器继续提示新命令。

默认情况下,提示包含两个整数。 第一个整数描述评估堆栈的深度(即,调用了多少次Tcl_Eval)。 第二个整数是Tcl历史标识符。 提示可以通过定义一个名为“prompt1”的过程来设置,该过程的返回值将成为下一个提示。 如果一条语句有开引号,括号,大括号或括号,则在换行符时会发出次要提示(默认为“+>”)。 辅助提示符可以通过定义称为“prompt2”的过程来设置。

解释器中 ,即使调用者使用原始模式,也会使用烹饪模式。

如果stdin被关闭, 解释器将返回,除非使用了-eof标志,在这种情况下调用后续的参数。

log_file [args] [[-a]文件]
如果提供了文件名, log_file将在文件中记录会话的记录( 从那一点开始)。 如果没有参数, log_file将停止记录。 任何以前的日志文件已关闭。

可以使用-open-leaveopen标志来提供Tcl文件标识符,而不是文件名。 这与spawn命令类似。 (见更多信息产生 。)

-a标志强制输出记录被log_user命令抑制。

默认情况下, log_file命令附加到旧文件而不是截断它们,以方便在一个会话中关闭和多次关闭日志。 要截断文件,请使用-noappend标志。

-info标志使得log_file返回给定的最近的非信息参数的描述。

log_user -info | 0 | 1
默认情况下,发送/预期对话被记录到标准输出(如果打开,则会记录一个日志文件)。 通过命令“log_user 0”禁用日志记录到stdout,并通过“log_user 1”重新启用日志记录。 记录到日志文件没有改变。

-info标志使得log_user返回给定的最近的非信息参数的描述。

match_max [-d] [-i spawn_id] [size]
定义期望内部使用的缓冲区大小(以字节为单位)。 如果没有大小参数,则返回当前大小。

使用-d标志设置默认大小。 (初始默认值是2000.)使用-i标志时,大小将被设置为指定的spawn ID,否则它将被设置为当前进程

叠加[ - #spawn_id] [ - #spawn_id] [...]程序[args]
执行“程序参数”代替当前的Expect程序,该程序终止。 一个纯粹的连字符参数在命令名之前强制使用连字符,就像它是一个登录shell一样。 除了那些以参数命名的spawn_ids之外,所有spawn_id都是关闭的 这些映射到指定的文件标识符。

Spawn_ids被映射到新程序继承的文件标识符。 例如,下面一行运行国际象棋,并允许它由当前进程控制 - 比如国际象棋大师。

覆盖-0 $ spawn_id -1 $ spawn_id -2 $ spawn_id国际象棋

这比“交互-u”更有效,然而,由于Expect 过程不再受控,它会牺牲进行编程交互的能力。

请注意,不提供控制终端。 因此,如果您断开或重新映射标准输入,执行作业控制(shell,登录等)的程序将无法正常运行。

奇偶校验[-d] [-i spawn_id] [值]
定义是否应从产生的进程的输出中保留或删除奇偶校验。 如果为零,则会剥离奇偶校验,否则不会剥离。 没有参数时,返回当前值。

使用-d标志,默认的奇偶校验值被设置。 (初始默认值为1,即奇偶校验未被剥离。)使用-i标志,将为指定的spawn id设置奇偶校验值,否则将为当前进程设置奇偶校验值。

remove_nulls [-d] [-i spawn_id] [value]
定义在模式匹配或存储在变量expect_outinteract_out之前,是否保留或从生成的进程的输出中删除了空值。 如果值为 1,则删除空值。 如果值为 0,则不删除空值。 没有参数时,返回当前值。

-d标志设置默认值。 (初始默认值为1,即空值被删除。)使用-i标志,将为指定的spawn id设置该值,否则将为当前进程设置该值。

无论是否删除空值, Expect都会将空字节记录到日志和标准输出中。

发送[-flags]字符串
字符串发送到当前进程 。 例如,该命令

发送“hello world \ r”

将字符helloworld发送到当前进程 。 (Tcl包含一个可以构建任意复杂字符串的类似printf的命令(称为格式 )。)

虽然带有行缓冲输入的程序在发送返回字符之前不会读取字符,但会立即发送字符。 返回字符用“\ r”表示。

-标志强制下一个参数被解释为一个字符串而不是一个标志。 任何字符串都可以以“ - ”开头,不管它是否看起来像一个标志。 这提供了一种可靠的机制来指定变量字符串,而不会被偶然看起来像标志的那些字符串绊倒。 (所有以“ - ”开头的字符串都保留给将来的选项。)

-i标志声明将该字符串发送到指定的spawn_id。 如果spawn_id是user_spawn_id ,并且终端处于原始模式,则字符串中的换行符将被转换为返回换行符序列,以使它们看起来好像终端处于煮熟模式。 -raw标志禁用此翻译。

-null标志发送空字符(0字节)。 默认情况下,发送一个null。 一个整数可以跟在-null后面以指示要发送多少个空值。

-break标志产生一个中断条件。 这只有在spawn id引用通过“spawn -open”打开的tty设备时才有意义。 如果你已经产生了一个过程 ,如提示,你应该使用提示的惯例产生一个休息。

-s标志强制输出将被“缓慢”发送,从而避免出现这种情况,即计算机对输入缓冲区进行了突破,这种输入缓冲区专为永远不会输入相同缓冲区的人设计。 该输出由变量“send_slow”的值来控制,该变量采用两个元素列表。 第一个元素是一个描述原子发送字节数的整数。 第二个元素是一个实数,它描述了原子发送必须被隔开的秒数。 例如,“set send_slow {10 .001}”将强制“send -s”在发送的每10个字符之间发送1毫秒的字符串。

-h标志强制输出被发送(有点),就像人类实际输入一样。 类似人物的延迟出现在角色之间。 (该算法基于威布尔分布,并进行修改以适应该特定应用。)该输出由变量“send_human”的值控制,该变量采用五元素列表。 前两个元素是以秒为单位的字符的平均到达时间间隔。 第一个默认使用。 第二个用于单词结尾,以模拟偶尔在这种转换中出现的细微暂停。 第三个参数是变异性的度量,其中.1变化很大,1是合理变化的,10是相当不变的。 极端是从0到无限。 最后两个参数分别是最小和最大间隔时间。 最后使用最小值和最大值,并将最后一次“剪辑”。 如果最小值和最大值限制足够的值,那么最终平均值可能与给定平均值有很大差异。

作为一个例子,下面的命令模拟一个快速和一致的打字员:

设置send_human {.1 .3 1 .05 2} send -h“我饿了,我们来做午餐吧。”

而在宿醉之后以下可能更适合:

设置send_human {.4 .4 .2 .5 100}发送-h“Goodd party lash night!”

请注意,虽然可以通过在发送参数中嵌入错误和更正来自己设置错误纠正情况,但不会模拟错误。

用于发送空字符,发送中断,强制慢输出和人类输出的标志是相互排斥的。 只有最后指定的那个将被使用。 而且,不能使用用于发送空字符或中断的标志来指定字符串参数。

按照期望将第一次发送发送流程是一个好主意。 期望等待进程启动,而发送不能。 特别是,如果第一次发送流程开始运行之前完成,您可能会忽略数据。 在交互式程序没有提供初始提示的情况下,您可以延迟发送 ,如下所示:

#为了避免给黑客提示如何入侵,#此系统不会提示输入外部密码。 #等待5秒让exec完成spawn telnet very.secure.gov sleep 5发送密码\ r

exp_send发送的别名 如果您在Tk环境中使用Expectk或Expect的其他变体,则发送由Tk定义,用于完全不同的目的。 exp_send是为了兼容环境而提供的。 为其他Expect的其他发送命令提供了类似的别名。

send_error [-flags]字符串
就像发送 ,除了输出发送到stderr而不是当前进程

send_log [ - ]字符串
就像发送 ,除了字符串只发送到日志文件(请参阅log_file 。)如果没有打开日志文件,参数将被忽略。

send_tty [-flags]字符串
就像发送 ,除了输出发送到/ dev / tty而不是当前进程

send_user [-flags]字符串
就像发送 ,除了输出发送到标准输出而不是当前进程

睡眠秒
导致脚本在给定的秒数内休眠 。 秒可能是十进制数。 在Expect休眠时处理中断(和Tk事件,如果您使用的是Expectk)。

spawn [args]程序[args]
创建一个运行“程序参数”的新进程 。 它的stdin,stdout和stderr被连接到Expect,以便它们可以被其他Expect命令读取和写入。 关闭时断开连接或者进程本身关闭任何文件标识符。

当一个进程spawn启动时,变量spawn_id被设置为引用该进程的描述符。 spawn_id描述的过程被认为是“当前 过程 ”。 spawn_id可以被读取或写入,实际上提供作业控制。

user_spawn_id是包含引用用户的描述符的全局变量。 例如,当spawn_id被设置为这个值时, expect的行为就像expect_user

.I error_spawn_id是一个包含描述符的全局变量,它指向标准错误。 例如,当spawn_id设置为这个值时, send的行为就像send_error

tty_spawn_id是一个包含描述符的全局变量,该描述符引用/ dev / tty。 如果/ dev / tty不存在(例如在cron,at或批处理脚本中),则tty_spawn_id未定义。 这可能会被测试为:

如果{[info vars tty_spawn_id]} {#/ dev / tty存在} else {#/ dev / tty不存在#可能在cron,批处理或脚本中}

spawn返回UNIX 进程 ID。 如果没有进程产生,返回0。 变量spawn_out(slave,name)被设置为pty从属设备的名称。

默认情况下, spawn回显命令名称和参数。 -noecho标志停止产生

-console标志使控制台输出重定向到派生进程 。 这在所有系统上都不受支持。

在内部, spawn使用一个pty,初始化方式与用户的tty相同。 这是进一步初始化,以便所有设置都是“健全的”(根据stty(1))。 如果定义了stty_init变量,则会以stty参数的形式将其解释为进一步的配置。 例如,“set stty_init raw”将导致进一步产生的进程的终端以原始模式启动。 -nottycopy跳过基于用户tty的初始化。 -nottyinit跳过了“理智”的初始化。

通常, 产卵需要很少的时间来执行。 如果您注意到产卵需要大量时间,可能会遇到被楔入的ptys。 许多测试运行在ptys上以避免与错误进程的纠缠。 (这些需要花费10秒的时间)。使用-d选项运行期望将显示Expect是否遇到奇怪状态下的许多ptys。 如果你不能杀死这些ptys所连接的进程,你唯一的办法就是重新启动。

如果由于exec(2)失败(例如程序不存在)而无法成功产生程序 ,则下一个交互expect命令将返回错误消息,就像程序已运行一样,并生成错误消息作为输出。 这种行为是spawn实现的自然结果。 在内部产生分叉,之后产生的进程无法与原始Expect 进程通信,除非通过spawn_id进行通信。

-open标志将导致下一个参数被解释为Tcl文件标识符(即,通过打开返回)。然后可以使用spawn id,就好像它是一个衍生进程 。 (不应再使用文件标识符。)这使您可以将原始设备,文件和管道视为派生进程而不使用pty。 返回0表示没有关联的进程 。 当与产生的进程的连接关闭时,Tcl文件标识符也是如此。 -leaveopen标志与-open相似,只是-leaveopen会导致文件标识符在spawn id关闭后保持打开状态。

-pty标志导致一个pty被打开,但没有进程产生。 返回0表示没有关联的进程 。 Spawn_id照常设置。

变量spawn_out(slave,fd)被设置为对应于pty slave的文件标识符。 它可以使用“close-slala”关闭。

-ignore标志命名在衍生过程中被忽略的信号。 否则,信号将获得默认行为。 除了每个信号需要单独的标志以外,信号的命名方式与陷阱命令相同。

strace级别
导致在执行之前打印以下语句。 (Tcl的跟踪命令跟踪变量。) level指示调用堆栈要跟踪多远。 例如,以下命令在跟踪前4个级别的调用时运行Expect ,但是不会在此之下。

期望-c“strace 4”script.exp

-info标志导致strace返回给定的最近的非信息参数的描述。

stty args
类似于外部stty命令改变终端模式。

默认情况下,控制终端被访问。 其他终端可以通过追加“请求状态返回命令的结果”来进行访问,如果没有请求状态并且控制终端被访问,原始和回显属性的先前状态将以一种形式返回,由命令使用。

例如,参数raw-cooked使终端进入原始模式。 参数-rawcooked使终端进入熟食模式。 参数echo-echo分别将终端设置为echo和noecho模式。

以下示例说明如何暂时禁用回显。 这可以在其他自动脚本中使用,以避免在其中嵌入密码。 (请参阅下面的预期提示下的更多讨论。)

stty -echo send_user“密码:”expect_user -re“(。*)\ n”set password $ expect_out(1,string)stty echo

系统参数
给出参数 sh(1)作为输入,就像它从终端输入命令一样。 期待等到壳终止。 sh的返回状态的处理方式与exec处理其返回状态的方式相同。

与将stdin和stdout重定向到脚本的exec不同系统不执行重定向(除了由字符串本身指示的重定向)。 因此,可以使用必须直接与/ dev / tty交谈的程序。 出于同样的原因, 系统的结果不记录在日志中。

时间戳[args]
返回一个时间戳。 没有参数时,返回从纪元开始的秒数。

-format标志引入了一个返回的字符串,但根据strftime的POSIX规则进行了替换。 例如,%a被缩写星期几名称(即星期六)取代。 其他是:

%a缩写星期几名称%A完整星期几名称%b缩写月份名称%B完整月份名称%c日期时间如下:Wed Oct 6 11:45:56 1993%d本月的某天(01-31%H小时(00-23)%I小时(01-12)%j日(001-366)%m月(01-12)%M分(00-59)%上午或下午%秒(00-61) %u日(星期一至星期一至星期一为星期几的第一天)%U周(00-53,第一个星期日为第一星期的第一天)%V星期(01-53,ISO 8601风格)%w日(0- 6)%W周(00-53,第一个星期一是第一个星期的第一天)%x日期时间如下:星期三Oct 6 1993%X时间:23:59:59%y年(00-99) %Y年,如:1993%Z时区(或者如果无法确定,则没有任何变化)%%光秃秃的百分号

其他%规格未定义。 其他角色将通过未触及的方式传递。 仅支持C语言环境。

-seconds标志引入了自时代起被用作格式化源的秒数。 否则,使用当前时间。

-gmt标志强制时间戳输出使用GMT时区。 没有标志时,使用本地时区。

陷阱[[命令]信号]
使未来收到任何给定信号时执行给定命令 。 该命令在全局范围内执行。 如果命令不存在,则返回信号动作。 如果命令是字符串SIG_IGN,则信号被忽略。 如果命令是字符串SIG_DFL,则信号是系统默认的结果。 信号可以是单个信号,也可以是一系列信号。 信号可以根据信号(3)以数字或符号的方式指定。 “SIG”前缀可以省略。

如果没有参数(或参数-number),则陷阱将返回当前正在执行的陷阱命令的信号编号。

-code标志使用命令的返回代码代替Tcl在命令最初开始运行时要返回的任何代码。

-interp标志使命令在命令开始运行时使用活动的解释器进行评估,而不是在声明陷阱时进行评估。

-name标志使陷阱命令返回当前正在执行的陷阱命令的信号名称。

-max标志使陷阱命令返回可以设置的最大信号编号。

例如,命令“trap {send_user”Ouch!“} SIGINT”将打印“Ouch!” 每次用户按^ C。

默认情况下,SIGINT(通常可以通过按^ C生成)和SIGTERM导致Expect退出。 这是由于以下陷阱,当Expect启动时默认创建。

trap exit {SIGINT SIGTERM}

如果使用-D标志启动调试器,则重新定义SIGINT以启动交互式调试器。 这是由于以下陷阱:

trap {exp_debug 1} SIGINT

通过将环境变量EXPECT_DEBUG_INIT设置为新的陷阱命令,可以更改调试器陷阱。

当然,您可以通过向脚本添加陷阱命令来覆盖这两者。 特别是,如果您有自己的“陷阱出口SIGINT”,这将覆盖调试器陷阱。 如果你想阻止用户进入调试器,这很有用。

如果您想在SIGINT上定义自己的陷阱,但仍然在调试器运行时陷入调试器,请使用:

if {![exp_debug]} {trap mystuff SIGINT}

或者,您可以使用其他信号捕获调试器。

陷阱不会让你重写SIGALRM的操作,因为这在内部用于Expect 。 disconnect命令将SIGALRM设置为SIG_IGN(忽略)。 只要在后续的spawn命令中禁用它,就可以重新启用它。

有关更多信息,请参阅信号(3)。

等待[args]
延迟到产生的进程 (或当前进程,如果没有被命名)终止。

通常等待返回一个四个整数的列表。 第一个整数是等待的进程的PID。 第二个整数是相应的spawn id。 如果发生操作系统错误,则第三个整数为-1,否则为0。 如果第三个整数是0,则第四个整数是生成的进程返回的状态。 如果第三个整数是-1,则第四个整数是操作系统设置的errno的值。 全局变量errorCode也被设置。

其他元素可能会出现在等待返回值的末尾。 可选的第五个元素标识一类信息。 目前,这个元素的唯一可能值是CHILDKILLED,在这种情况下,接下来的两个值是C风格的信号名称和简短的文字描述。

-i标志声明进程等待对应于命名的spawn_id(不是进程 ID)。 在SIGCHLD处理程序中,可以通过使用spawn id -1来等待任何产生的进程

-nowait标志会导致等待立即返回并显示成功等待。 当进程退出(稍后)时,它将自动消失而不需要明确的等待。

等待命令也可以使用参数“-i -1”等待分叉进程 。 与使用衍生进程不同,该命令可以随时执行。 无法控制收购哪个流程 。 但是,可以检查进程 ID的返回值。

图书馆

Expect自动知道Expect脚本的两个内置库。 这些由变量exp_library和exp_exec_library中命名的目录定义。 两者都是为了包含其他脚本可以使用的实用程序文件。

exp_library包含与体系结构无关的文件。 exp_exec_library包含与架构相关的文件。 根据您的系统,这两个目录可能完全是空的。 文件$ exp_exec_library / cat-buffers的存在描述了你的默认/ bin / cat缓冲区。

良好的格式打印

vgrind定义可用于漂亮的Expect脚本。 假设Expect发行版随附的vgrind定义已正确安装,您可以将其用作:

vgrind -lexpect文件

例子

很多不太明显的如何将手册页描述的所有内容放在一起。 我鼓励您阅读并尝试Expect发行版示例目录中的示例。 其中有些是真正的节目。 其他人只是说明某些技术,当然,一对夫妇只是快速入侵。 INSTALL文件可以快速浏览这些程序。

期望论文(参见另请参阅)也很有用。 虽然有些论文使用与早期版本的Expect相对应的语法,但随附的基本原理仍然有效,并且比本手册页更详细。

CAVEATS

扩展可能与Expect的命令名称相冲突。 例如, send由Tk定义,用于完全不同的目的。 出于这个原因,大多数Expect命令也可用作“exp_XXXX”。 以“exp”,“inter”,“spawn”和“timeout”开头的命令和变量没有别名。 如果需要环境之间的兼容性,请使用扩展的命令名称。

预期对于范围界定需要相当自由的观点。 特别是,通过特定于Expect程序的命令读取的变量将首先从本地范围中寻找,如果未找到,则在全局范围中寻找。 例如,这可避免在您编写的使用期望的每个过程中放置​​“全局超时”。 另一方面,写入的变量总是在本地范围内(除非发布了“全局”命令)。 这导致最常见的问题是在过程中执行spawn时。 在过程之外, spawn_id不再存在,所以产生的过程不再仅仅因为作用域而被访问。 为这样的程序添加一个“全球spawn_id”。

如果您无法启用多次展示功能(即您的系统不支持select(BSD *。*),poll(SVR> 2)或其他类似功能),则Expect一次只能控制一个进程 。 在这种情况下,不要试图设置spawn_id ,也不应该在生成的进程运行时通过exec执行进程。 此外,您将无法同时期望多个进程(包括一个用户)。

终端参数对脚本有很大的影响。 例如,如果编写脚本查找回显,如果关闭回显,则脚本会显示错误。 为此,Expect默认强制终端参数。 不幸的是,这会让其他程序变得不愉快。 例如,emacs shell想要更改“常规”映射:新行映射到换行符而不是回车换行符,并且禁用回显。 这允许使用emacs编辑输入行。 不幸的是,Expect不可能猜到这一点。

您可以请求Expect不会覆盖其默认的终端参数设置,但在编写此类环境的脚本时,您必须非常小心。 在emacs的情况下,避免取决于回显和行结束映射等内容。

接受参数的命令放入单个列表( 期望变体和交互 )使用启发式来确定列表实际上是一个参数还是多个参数。 只有当列表实际上代表一个具有多个嵌入\ n的非空白字符的参数时,启发式才会失败。 这似乎非常不可能,但是可以使用参数“-nobrace”来强制将单个参数作为单个参数进行处理。 这可以想象用于机器生成的预期代码。 同样,-brace强制将单个参数作为多个模式/动作进行处理。

BUGS

名为“性”的程序(无论是“Smart EXec”还是“Send-Expect”)都是很诱人的,但是良好的意义(或者也许只是清教主义)占上风。

在某些系统上,当shell被产生时,它会抱怨无法访问tty,但仍然运行。 这意味着你的系统有一个获得Expect不知道的控制权的机制。 请找出它是什么,并将这些信息发回给我。

Ultrix 4.1(至少这里的最新版本)认为1000000以上的超时等于0。

如果您定义了一个SIGCHLD处理程序,Digital UNIX 4.0A(可能还有其他版本)拒绝分配ptys 。 请参阅grantpt页面以获取更多信息。

IRIX 6.0不能正确处理pty权限,因此如果Expect尝试分配先前由其他人使用的pty,则它会失败。 升级到IRIX 6.1。

如果未设置TERM,Telnet(仅在SunOS 4.1.2下验证)会挂起。 这是cron,at和cgi脚本下的问题,它们没有定义TERM。 因此,你必须明确地设置它 - 哪种类型通常是不相关的。 它只需要设置一些东西! 以下可能足以满足大多数情况。

设置env(TERM)vt100

如果没有设置SHELL和HOME,提示(只在BSDI BSD / OS 3.1 i386下验证)挂起。 这是 cron ,at和cgi脚本下的问题,它们没有定义这些环境变量。 因此,你必须明确地设置它们 - 哪种类型通常是不相关的。 它只需要设置一些东西! 以下可能足以满足大多数情况。

设置env(SHELL)/ bin / sh设置env(HOME)/ usr / local / bin

ptys的一些实现被设计为在内核关闭文件描述符后,内核在10到15秒后抛出所有未读输出(实际数字取决于实现)。 因此, 预计如程序

产卵日期睡眠20期望

将失败。 为了避免这种情况,使用exec调用非交互式程序而不是产生 。 虽然这种情况是可以想象的,但实际上我从来没有遇到过由于这种行为而导致真正的交互式程序的最终输出会丢失的情况。

另一方面,Cray UNICOS ptys在进程关闭文件描述符后立即丢弃任何未读输出。 我已经向Cray报告了这些情况,他们正在努力解决问题。

有时在提示和响应之间需要延迟,例如当tty接口通过查找开始/停止位来改变UART设置或匹配波特率时 。 通常,所有这些都需要睡一两秒钟。 一个更强大的技术是重试,直到硬件准备好接收输入。 以下示例使用两种策略:

发送“speed 9600 \ r”; sleep 1 expect {timeout {send“\ r”; exp_continue} $提示符}

trap -code不能用于任何位于Tcl事件循环中的命令,如睡眠。 问题是,在事件循环中,Tcl放弃了来自异步事件处理程序的返回码。 解决方法是在陷阱代码中设置一个标志。 然后在命令后立即检查标志(即睡眠)。

expect_background命令忽略-timeout参数,并且通常没有超时的概念。

"预期提示"

有一些关于Expect的内容可能不直观。 本节试图用一些建议来解决其中的一些问题。

常见的期望问题是如何识别shell提示。 由于这些定制方式因不同的人和不同的shell而不同,因此在不知道提示的情况下,可移植性自动化rlogin可能会很困难。 一个合理的惯例是让用户在环境变量EXPECT_PROMPT中存储一个正则表达式来描述他们的提示(特别是它的结束)。 可以使用类似下面的代码。 如果EXPECT_PROMPT不存在,代码仍然有正常运行的良好机会。

设置提示符“(%|#| \\ $)$”;#默认提示符catch {set prompt $ env(EXPECT_PROMPT)} expect -re $ prompt

我鼓励你写预期模式,包括任何你期望看到的结局。 这避免了在看完整个事情之前回答问题的可能性。 另外,虽然你完全可以在看到它们之前回答问题,但如果你提前回答,你的回答可能会在问题中间回显。 换句话说,最终的对话将是正确的,但看起来很混乱。

大多数提示最后都包含空格字符。 例如,来自ftp的提示是'f','t','p','>'和。 要匹配此提示,您必须考虑每个这些字符。 不包括空白是一个常见的错误。 明确地填入空格。

如果使用X *形式的模式,*将匹配从X末尾接收到的所有输出到最后一次接收到的输出。 这听起来很直观,但可能有点令人困惑,因为短语“最后收到的东西”可能因计算机的速度以及内核和设备驱动程序的I / O处理而异。

特别是,当实际上大多数节目一次产生一行输出时,人类倾向于看到节目输出以大块(原子)方式到达。 假设情况如此,前一段的模式中的*可能只与当前行的结尾相匹配,即使似乎有更多,因为在匹配时,这是所有已收到的输出。

除非你的模式特别说明它,否则期望无法知道进一步的输出即将到来。

即使取决于线路缓冲是不明智的。 不仅程序很少会对他们所做的缓冲类型作出承诺,而且系统消化不良可能会破坏输出线,从而使线在看似随机的地方断裂。 因此,如果在编写模式时可以表示提示的最后几个字符,那么这样做是明智的。

如果您正在等待程序的最后一个输出中的模式,并且该程序会发出其他内容,您将无法使用timeout关键字来检测该模式。 原因是期望不会超时 - 相反,它会得到一个eof指示。 改为使用它。 更好的是,同时使用两者。 这样,如果该线路四处移动,则不必编辑线路本身。

换行符通常会在终端驱动程序输出时转换为回车符,换行符序列。 因此,如果你想要一个明确地匹配printf(“foo \ nbar”)这两行的模式,你应该使用模式“foo \ r \ nbar”。

通过expect_user从用户读取时发生类似的翻译。 在这种情况下,当您按回车时,它将被转换为换行符。 如果Expect将其传递给一个将其终端设置为原始模式(如telnet)的程序,则会出现问题,因为该程序需要真正的返回。 (有些程序实际上是宽容的,因为它们会自动将换行符转换为返回值,但大多数程序不会)。不幸的是,无法确定程序是否将其终端设置为原始模式。

解决办法是使用命令“stty raw”,这将停止翻译。 但请注意,这意味着您将不再获得熟悉的行编辑功能。

交互隐式设置你的终端为原始模式,所以这个问题不会出现。

Expect脚本中存储密码(或其他私人信息)通常很有用。 这是不推荐的,因为任何存储在电脑上的东西都容易被任何人访问。 因此,交互式地从脚本中提示密码是比从字面上嵌入它们更聪明的想法。 尽管如此,有时这种嵌入是唯一的可能性。

不幸的是,UNIX文件系统没有创建可执行但不可读的脚本的直接方式。 支持setgid shell脚本的系统可以间接模拟如下:

像往常一样创建Expect脚本(包含秘密数据)。 使其权限为750(-rwxr-x ---),并由一个受信任的组(即允许读取它的组)拥有。 如有必要,请为此创建一个新组。 接下来,创建一个具有与之前相同的组的权限2751(-rwxr-s-x)的/ bin / sh脚本。

结果是一个可以被任何人执行(并阅读)的脚本。 当被调用时,它运行Expect脚本。

"另请参阅"

Tcl (3), libexpect (3)
“Exploring Expect:基于Tcl的工具包,用于自动化交互式程序” ,Don Libes,第602页,ISBN 1-56592-090-2,O'Reilly and Associates,1995。
“期望:治愈那些无法控制的交互性的适应”,Don Libes, 1990年夏季刊,美国加利福尼亚州阿纳海姆1990年6月11 - 15日的会议论文集。
.I“使用期望自动化系统管理任务”,由Don Libes于1990年10月17 - 19日在科罗拉多州科罗拉多斯普林斯举行的1990 USENIX大型安装系统管理会议论文集中提供。
.I“Tcl:一种可嵌入的命令语言”作者John Ousterhout 1990年美国华盛顿特区1990年1月22 - 26日在美国华盛顿举行的会议论文集“我期望:控制交互式程序的脚本”,由Don Libes,Computing Systems ,Vol。 4,No.2,加利福尼亚大学出版社,1991年11月.I。“回归测试和一致性测试交互式程序”,由Don Libes出版,1992年夏季刊USENIX Conference,135-144页,San Antonio,TX, 1992年6月12日至15日..我“Kibitz - 连接多个交互式程序”,作者:Don Libes,Software - Practice&Experience,John Wiley&Sons,West Sussex,England,Vol。

1993年5月第23卷第5期.I'Teporger for Tcl Applications“,Don Libes,1993年Tcl / Tk Workshop,1993年6月10 - 11日加利福尼亚州伯克利的论文集。

作者

Don Libes,国家标准与技术研究院

致谢

感谢Tcl的John Ousterhout和Scott Paisley的灵感。 感谢Rob Savoye为Expect的自动配置代码。

HISTORY文件记录了期望的大部分演变。 它使有趣的阅读,并可能让你进一步洞察这个软件。 感谢其中提到的人谁给我错误修复,并给予其他帮助。

Expect的设计和实施部分由美国政府支付,因此属于公共领域。 然而,如果使用这个程序和文档或者其中的一部分,作者和NIST希望得到信任。