Expect简介
Expect 是一个可以通过脚本和其它交互程序通信的程序,也可以直接被用于C或C++。
可以实现什么?
- 让计算机自动回应,比如你可以登录,无需手动输入
- 开始一个游戏,如果最佳配置没有出现,一直重启直到他出现,然后把控制权交给你。
- 基于预先确定的标准,运行回话,回答问题“是”、“否”,然后把控制权交给你
- 链接到其它站点或者BBS,自动检索信息发送邮件到本地系统。
- 传递环境变量,当前路径或者任何类型的信息通过rlogin, telnet, tip, su, chgrp等
安装
yum install expect
用法
expect [ -dDinN ] [ -c cmds ] [ -[f|b] ] cmdfile ] [ args ]
Expect从cmdfile文件读取一组命令来执行,Expect还可以在支持#!的系统上隐式调用。通过将脚本标记为可执行文件,并将脚本中的第一行标记为可执行文件。
#!/usr/local/bin/expect -f
#方式一,如上,放在文件的开头,其中路径/usr/local/bin/expect是Expect存在的路径,文件一般以*.exp结尾。
-c标志把要执行的命令置于脚本中任何命令之前。命令应该用引号括起来,以防止被shell破坏。此选项可多次使用。通过用分号分隔多个命令,可以用一个-c执行多个命令。命令按照它们出现的顺序执行。(在使用Expectk时,此选项被指定为-command。)
-d标志启用一些诊断输出,主要报告expect和interaction等命令的内部活动。这个标志与Expect脚本开头的“exp_internal 1”具有相同的效果,还会打印Expect的版本。(strace命令用于跟踪语句,trace命令用于跟踪变量分配)(在使用Expectk时,该选项被指定为-diag)。
-D标志启用交互式调试器。后面应该是整数值。如果值非零,或者按下了^C(或者命中了一个断点,或者脚本中出现了其他适当的调试器命令),调试器将在执行下一个Tcl过程之前进行控制。有关调试器的更多信息,请参阅README文件或参阅(下面)。(在使用Expectk时,此选项被指定为-Debug。)
-f标志前置一个文件,从其中读取命令。这个标志本身是可选的,因为它只在使用#!表示法(参见上面),以便在命令行上提供其他参数。(在使用Expectk时,此选项被指定为-file。)
默认情况下,命令文件被读入内存并全部执行。有时需要一次读取一行文件。例如,以这种方式读取stdin。为了强制以这种方式处理任意文件,请使用-b标志。(在使用Expectk时,此选项被指定为-buffer。)
如果字符串"-"作为文件名提供,则读取标准输入。(使用”。/-"从一个实际名为"-"的文件中读取。)
-i标志期望交互式地提示命令,而不是从文件中读取命令。提示通过exit命令或EOF终止。更多信息请参见解释器(下面)。如果既不使用命令文件也不使用-c,则假定为-i。(在使用Expectk时,此选项被指定为-interactive。)
--可用于分隔选项的结束。如果希望向脚本传递类似选项的参数,而不让Expect解释该参数,那么这将非常有用。这可以有效地放在#!线,以防止任何标志的解释,由期望。例如,下面将保留变量argv中的原始参数(包括脚本名)。
#!/usr/local/bin/expect --
注意,在向#!添加参数时,必须遵守通常的getopt(3)和execve(2)约定#! line。
文件$ exp_library /expect.rc,如果存在rc,则会自动获取,除非使用-N标志。(在使用Expectk时,此选项被指定为-NORC。)紧接其后的是~/.expect文件。rc的来源是自动的,除非使用-n标志。如果定义了环境变量DOTDIR,则将其视为一个目录和.expect。rc从那里读取。(在使用Expectk时,此选项被指定为-norc。)此溯源仅在执行任何-c标志之后发生。
-v 表示期望打印其版本号并退出。(在使用长标志名的Expectk中,对应的标志是-version。)
可选参数args被构造成一个列表并存储在名为argv的变量中。argc被初始化为argv的长度。
argv0被定义为脚本的名称(如果不使用脚本,则为二进制)。例如,下面将打印出脚本的名称和前三个参数:
send_user "$argv0 [lrange $argv 0 2]\n"
常用命令
Expect使用Tcl(工具命令语言)。Tcl提供了控制流(例如,if、for、break)、表达式求值和其他一些特性,如递归、过程定义等。这里使用但未定义的命令(例如,set, if, exec)是Tcl命令(参见Tcl(3))。Expect支持下面描述的其他命令。除非另外指定,否则命令返回空字符串。
命令按字母顺序排列,以便快速定位。然而,新用户可能会发现,按照顺序阅读衍生、发送、期望和交互的描述会更容易。
注:大写“E”的“Expect”指的是Expect程序,而小写“E”的“Expect”指的是Expect程序中的Expect命令
expect [[-opts] pat1 body1] ... [-opts] patn [bodyn]
等待,直到其中一个模式与生成的进程的输出匹配、经过了指定的时间段或看到文件结束。如果最后的主体是空的,它可以被省略。
如果整个expect语句的参数需要不止一行,那么所有参数都可以“加固”为一行,以避免每行都以反斜杠结束。在这种情况下,尽管有括号,通常的Tcl替换仍然会发生。
如果模式是关键字eof,则在文件结束时执行相应的主体。如果模式是关键字超时,则在超时时执行相应的主体。如果没有使用超时关键字,则在超时时执行隐式空操作。默认超时时间为10秒,但是可以通过命令“set timeout 30”将其设置为30秒。可以通过值-1指定无限超时。如果模式是关键字默认值,则在超时或文件结束时执行相应的主体。
如果模式匹配,则执行相应的主体。expect返回主体的结果(如果没有匹配的模式,则返回空字符串)。在多个模式匹配的情况下,首先出现的模式用于选择主体。
每次新输出到达时,它将按照列出的顺序与每个模式进行比较。因此,您可以通过确保最后一个模式出现(比如提示符)来测试是否存在匹配。在没有提示的情况下,必须使用超时(就像手动交互时一样)。
模式通过三种方式指定。默认情况下,使用Tcl的字符串匹配命令指定模式。(这种模式也类似于C-shell正则表达式,通常称为“glob”模式)。gl标志可以用来保护那些可能与expect标志匹配的模式。任何以“-”开头的模式都应该这样保护。(所有以“-”开头的字符串都保留为以后的选项。)
-timeout标志使当前的expect命令使用以下值作为超时,而不是使用超时变量的值。
exp_continue命令允许expect本身继续执行,而不是像通常那样返回。
interact 将当前进程的控制权交给用户,以便将击键发送给当前进程,并返回当前进程的stdout和stderr。
log_file [args] [[-a] file] 如果提供了文件名,log_file将在文件中记录会话的记录(从此时开始)。如果没有给出参数,log_file将停止记录。关闭以前的任何日志文件。
log_user -info|0|1 默认情况下,send/expect对话被记录到标准输出(如果打开,则记录一个日志文件)。
overlay [-# spawn_id] [-# spawn_id] [...] program [args] 执行程序参数代替当前的Expect程序,后者将终止。
send [-flags] string 发送字符串到当前进程
#示例1: 下面的片段寻找一个成功的登录
expect {
busy {puts busy\n ; exp_continue}
failed abort
"invalid password" abort
timeout abort
connected
}
#示例2:第四个模式必须使用引号,因为它包含一个空格,否则将把模式与操作分开。具有相同动作的模式(如#第3次和第4次)需要再次列出动作
expect {
busy {puts busy\n ; exp_continue}
-re "failed|invalid password" abort
timeout abort
connected
}
#示例3:等待当前进程中的“connected”,或从名为$proc2的spawn_id中等待“busy”、“failed”或“invalid password”。
expect {
-i $proc2 busy {puts busy\n ; exp_continue}
-re "failed|invalid password" abort
timeout abort
connected
}
exit [-opts] [status]
预期退出或准备退出,
-onexit标志使下一个参数用作退出处理程序。如果没有参数,则返回当前退出处理程序
status状态(如果未指定,则为0)作为Expect的退出状态返回。如果到达脚本的末尾,则隐式执行exit。
spawn [args] program [args]
创建一个新的进程运行程序args。它的stdin、stdout和stderr连接到Expect,以便其他Expect命令可以读写它们。关闭或进程本身关闭任何文件标识符将断开连接。
当进程由spawn启动时,变量spawn_id被设置为引用该进程的描述符。由spawn_id描述的进程被认为是当前进程。可以读取或写入spawn_id,实际上提供了作业控制。
wait [args]
延迟,直到派生的进程(如果没有命名,则为当前进程)终止。
wait通常返回一个由四个整数组成的列表。第一个整数是等待的进程的pid。第二个整数是对应的衍生id。第三个整数是-1,如果操作系统出错,否则为0。如果第三个整数为0,则第四个整数是派生的进程返回的状态。如果第三个整数是-1,那么第四个整数是操作系统设置的errno值。还设置了全局变量errorCode。
其他元素可能出现在wait的返回值的末尾。第五个可选元素标识一类信息。目前,该元素唯一可能的值是CHILDKILLED,在这种情况下,接下来的两个值是c样式的信号名和一个简短的文本描述.
-i标志声明等待的进程对应于已命名的spawn_id(不是进程id)。在SIGCHLD处理程序中,可以使用派生id -1来等待派生的进程。
nowait标志使等待立即返回,表示等待成功。当进程退出(稍后)时,它将自动消失,不需要显式等待。
还可以使用wait命令使用参数“-i -1”来等待派生进程。与对派生进程的使用不同,该命令可以在任何时候执行。无法控制哪一个过程被收获。但是,可以检查返回值的进程id。
实例
以下为我写的publish_TestTo73.exp通过sftp上传文件,并执行ssh脚本
#!/usr/bin/expect
set TARGET [lindex $argv 0]
set PORT [lindex $argv 1]
set USER [lindex $argv 2]
set PASSWD [lindex $argv 3]
set FILEPATH [lindex $argv 4]
set FILENAME [lindex $argv 5]
set APPNAME [lindex $argv 6]
set SERVER1 [lindex $argv 7]
set timeout 600
#sftp upload jar file
spawn sftp -P 2222 $USER@$TARGET
expect {
"yes/no" {send "yes\r";exp_continue}
"*password:" {send "$PASSWD\r";exp_continue}
"sftp>" {send "put $FILEPATH$FILENAME /$SERVER1/lipp/\r";exp_continue;}
"*100%*" {send "exit\r"}
}
#expect "100%"
#send "exit\r"
#ssh run a command
spawn ssh $USER@$TARGET -p $PORT
expect {
"*yes/no" {send "yes\r"; exp_continue}
"*password:" {send "$PASSWD\r";exp_continue}
"*Opt*" {send "$SERVER1\r";exp_continue}
"*shtermuser*" {send "sudo /data/docker-project/jenkinsStartJar.sh $FILENAME /tmp/lipp/\r"}
}
#interact
expect "*stopped*"
send "exit\r"
expect "Opt> "
send "exit\r"
expect eof
然后新增一个shell内可执行这个expect脚本
expect -f $curpath/publish_TestTo73.exp $ipaddr $port $username $passwd $filepath $filename $appname $SERVER1
参考资料:
expect基础语法: http://www.tcl.tk/man/expect5.31/expect.1.html#lbAB