Posts 学习笔记-Expect
Post
Cancel

学习笔记-Expect

1 Expect 脚本语法

1.1 Expect 脚本初窥

一个简单的输入密码操作

1
2
3
4
5
6
7
#!/usr/bin/expect –f 
set timeout 100
set password "123456"
spawn sudo rm -rf zzlogic
expect "root123456"
send "$password\n"
interact

1.2 Expect 命令

1.2.1 spawn

Func: 启动新的进程 spawn命令是Expect的初始命令,它用于启动一个进程,之后所有expect操作都在这个进程中进行,如果没有spawn语句,整个expect就无法再进行下去了。
spawn ssh root@192.168.0.1

1.2.2 send

Func : 用于向进程发送字符串;
eg: [send "ispass\r"]
注意:命令结尾加 \t或者 \n\t表示回车,\n表示换行。

send命令有几个可用的参数:

  • -i $spawn_id:这个参数用来向不同spawn_id的进程发送命令,是进行多程序控制的关键参数。
  • -s: s代表slowly,也就是控制发送的速度,这个参数使用要与expect中的变量send_slow相关联。

1.2.3 expect

Func: 从进程接收字符串,用于等候一个相匹配的输出,一旦匹配就执行后面的动作。
eg: expect "password:"
使用方法:
expect 表达式 动作 表达式 动作 ………………

eg :
expect "hi\n"send "hello there!\n"
从标准输入中等到hi和换行键后,向标准输出输出hello there。

  • $expect_out(buffer)存储了所有对expect的输入,$expect_out(0,string)存储了匹配到expect参数的输入。
  • expect *; # Receive anything returned immediately.
ElementsSignificance
expect_out(0,start)Index of the first character of the string that matched the entire expression
expect_out(0,end)Index of the last character of the string that matched the entire expression
expect_out(0,string)String that matched the entire expression
expect_out(1..9,start)Index of the first character of the string that matched the pattern enclosed in the 1st - 9th set of parentheses
expect_out(1..9,end)Index of the last character of the string that matched the pattern enclosed in the 1st - 9th set of parentheses
expect_out(1..9,string)String that matched the pattern enclosed in the 1st - 9th set of parentheses
expect_out(buffer)Entire contents of the buffer when a match was found
expect_out(spawn_id)Spawn id of the process which produced the matching pattern
  1. 直接使用:
    1
    2
    3
    4
    5
    6
    
    spawn ftp connect
     set spid_ftp $spawn_id
     spawn telnet connect
     set spid_telnet $spawn_id
     exp_send –i $spid_ftp “ftp command”
    exp_send –i $spid_telnet “telnet command”
    
  2. expect过程调用
    1
    2
    3
    4
    5
    6
    
    expect {
    -i $spid
    -timeout "your timeout"
    -re ".+" {append result $expect_out(buffer)}
    -re "other exit condition" {}
    }
    
    • -i选项
      -i选项一般用于同时控制多个spawn进程,通过这个选项向不同spawn_id发送命令就可以同时控制多个进程了
  • -d选项 几乎所有expect命令都支持-d选项,d代表default,也就是设置某些命令的默认值,比如:
    1
    2
    
    remove_nulls –d 1  
    timeout –d 30
    
  • -re选项
    re代表regular expression,也就是正则表达式,用于expect、interact命令族中,这种类型的表达式具有匹配一切字符的能力。

1.2.4 interact

Func: 允许用户交互。interact命令是Expect中非常重要的命令之一,它用来在脚本中将控制权交给用户,由用户与spawn生成的进程进行交互。 退出expect返回终端,可以继续输入,否则将一直在expect不能退出到终端。

1.2.5 exit

exit命令功能很简单,就是直接退出脚本,但可以利用这个命令对脚本做一些扫尾工作

1.2.6 set timeout

设置超时时间,单位是秒 (s);expect超时等待的时间。默认timeout为10s。

1
set timeout  -1  ##设置为永不超时

1.2.7 log_user

1
log_user 1    #是否显示脚本输出信息 1代表显示,0代表不显示

主要针对send命令,使得命令执行结果不打印,但是不影响puts输出

1.2.8 match_max

1
match_max 100000  ##设置缓冲区大小,单位:字节。

1.2.9 log_file

1
log_file expectout.txt

1.2.10 sleep and after

There are three different entities:

The Tclx’s sleep

The sleep command from the Tclx package. According to the documentation, it takes a decimal argument, taken to be the number of seconds to sleep. However, the fraction part is truncated. That means sleep 2.5 will sleep for two seconds.

The Expect’s sleep The sleep command from the Expect package. This is similar to its counterpart from the Tclxpackage. However, sleep 2.5 means sleeping for 2.5 seconds, there is no truncation.

After

Finally, the built-in after, which is a totally different beast. The after command takes its first input as the number of milliseconds to sleep off. This is the “synchronous” mode Jerry refers to. After also takes a second argument. In this case, after returns a token right away. After the specified time, the script will be executed. With the token, you can cancel the script.

I hope this helps.

My try at a short explanation:

The Tcl sleep will like the TclX sleep just pause the script.

The after command can pause the script, but it is normally used for event based programming. It can execute a script after the elapsed time (if the event loop is running).

More on this see here at beedub.com.

1.2.11 wait

首先spawn会开启一个子进程(spawn_id)去执行命令,expect eof用于等待进程结束,另外有一个close命令用于直接结束子进程(根据spawn_id来定位子进程)。问题在于eofclose都只是杀死子进程,但子进程变为僵尸进程依然存在进程列表中,僵尸进程会占用句柄,但是句柄是有限的,大量僵死进程的产生,导致整个脚本进程无法继续运行,报错退出。
网上的例子大多是流程式的脚本,不是本文这种循环运行的,所以等待eof后,主进程退出,将僵尸进程也回收了,因此不会有任何问题。 不退出主进程,还要及时回收僵尸子进程,很多语言都内置了相关的方法,expect脚本也不例外,wait就是负责给子进程收尸的。所以文章开头的脚本应该加上wait来及时回收telnet僵尸进程。

另外close, wait可以隐含地产生。expectinteract都能检测到当前程序的退出,并隐含地执行一个close,但是wait只会在父进程退出时(即脚本退出时)隐含产生。所以对于只有一个子进程的脚本在exit后可以不写wait. 

1.2.12 close a subprocess

(1) expect eofinteract都能检测到当前程序的退出,并隐含地执行一个close;

1
2
3
4
5
6
7
close does not call wait since there is no guarantee that closing a process connection will 
cause it to exit.
Fortunately, in many scripts it is not necessary to explicitly close the connection because 
it can occur implicitly. There are two situations when you do not have to use close: 

• when the Expect process ends, or 
• when the expect command reads an eof from the spawned process.

Close之后spawned process会die,结束子进程。
After closing the connection, a spawned process can finish up and exit.

(2) Wait用来清理子进程,

1
2
3
4
5
6
7
8
9
10
11
The -nowait flag causes the wait to return immediately with the indication of a successful wait.
 When the process exits (later), it will automatically disappear without the need for an explicit 
 wait.
Because a process will not disappear from the system until you give the wait command, it is 
common to speak of waiting for or waiting on a process. Some people also like to use the term 
reap as in "reaping a process".

Unlike close, however, wait implicitly happens in only one case-when an Expect process (i.e., 
script) exits. On exit, all the spawned processes are waited for.
This means that Expect scripts that only spawn a single process and then exit, need not call 
wait since it will be done automatically

只有一个子进程的程序不需要显式调用close/wait,Expect主进程结束时会隐式调用closewait.

1
2
3
wai t returns a list describing a process that was waited upon. 
The list contains the process id, spawn id, and a 0 or -1. A 0 indicates that the process was 
waited upon successfully. In this case, the next value is the status.

(3) Kill
Once killed, the process connection should be recycled by calling close and wait.
(4)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/expect -f

set spawn_id 0

set expectStr ""

spawn /bin/bash
expect "# $"
match_max 100000

# Now, start switchd with the new skip flags.
log_user 0
set timeout 120
send -i $spawn_id ""
expect {
    -i $spawn_id $expectStr { after 10 }
    timeout { return -code error "FATAL ERROR" }
}

expect -timeout 1 -i $spawn_id $prompt

send -i $spawn_id "\r"
expect -timeout 1 -i $spawn_id $prompt

log_user 1
set pid [exp_pid -i $spawn_id]  # exp_pid命令用来获取当前spawn的进程号,支持-i选项,用来指定具体的spawn进程
exec kill -9 $pid
puts "\n"

1.3 命令行参数

The script name is not included in the argument list. The arguments are only those of the script, not of Expect. This is convenient in many scripts because the argument list can be directly used without stripping out the command name.

The script name is stored in the variable argv0. Adding the command “puts “argv0 : $argv0 “” as the first line of the example script causes it to print an additionalline:

1
2
3
4
argvO: eeho.exp 
arg 0: faa 
arg 1: bar 
arg 2: 17 and a half

Very old systems do not follow the #! convention at all, and instead use /bin/ sh to execute all scripts. Inserting the following lines at the beginning of your script allows it to be portable between such systems and modern ones that do invoke the correct interpreter.t

1
2
3
4
5
6
#!/bin/sh 
set kludge { ${1+"$@"} 
shift 
shift 
exec expect -f $0 ${1+"$@"} 
} # rest of script follows

1.4 parameters

Flags that Expect knows about are:

FlagsDescription
-bread the script a line at a time (i.e., unbuffered)
-c cmdexecute this command before any in the script
-f fileread commands from this file
-dprint internal (diagnostic) information , exp_internal 1
-Denable the debugger
-irun interactively
-ndo not source - / . expect. Rc
-Ndo not source $expect_library / expect. Rc
-read commands from the standard input
--do not interpret remaining arguments, The -- is a flag to Expect. It says not to interpret any of the script arguments but just to pass them on to the script.

The -f flag prefaces a file from which to read commands from.
The flag itself is optional as it is only useful when using the #! notation (see above), so that other arguments may be supplied on the command line.
在脚本第一行解释器处加上这个参数,可以使得所有的-c选项能够被识别为参数。
Just as with --, when a script starts out with this -f line and is invoked just by its name (without “expect”), it behaves as you had entered the following command:

1
% expect -f script args   

Now you can use Expect flags such as -c and they will be correctly handled. Since the -f script looks like a flag, Expect continues looking and finds the -c and interprets this as a flag, too.

If you give the -c before the script name, it will not be included in the argv variable when the script ultimately gets control.

1.5 debug

1.6 send slow

1
2
3
4
5
LIB/common_lib.tcl
proc console_login { ts_IP_sup1 ts_port_sup1 {sup_pw 0}} {

set send_slow {2 .1}
send -s -i $sessionID "$bootString\r"
This post is licensed under CC BY 4.0 by the author.