@cdmonkey
2016-12-12T17:34:28.000000Z
字数 5780
阅读 1233
基础文档
http://blog.chinaunix.net/uid-25324849-id-3191069.html
Expect是基于Tcl的相对简单的一个免费的脚本编程工具语言,用来实现自动和交互式任务程序进行通信,无需人工进行干预。例如SSH、FTP等,这些程序正常情况下都需要手工与它们进行交互,而使用Expect就可以模拟手工交互的过程来实现自动的交互。
我们通过Shell可以实现简单的控制流功能,如:循环、判断等。但是对于需要交互的场合则必须通过人工来干预,有时候我们可能会需要实现自动和交互程序进行交互的功能,而Expect就使用来实现这种功能的工具。Expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。
Expect的安装非常的简单,通过yum进行安装即可:
[root@WEB-A1 ~]# yum install -y expect
[root@WEB-A1 ~]# rpm -qa|grep expect
expect-5.44.1.15-5.el6_4.x86_64
我们先举一个小的示例,如果我们通过ssh指令连接到远程主机,一般情况下需要我们手动输入用户密码。
[root@WEB-A1 ~]# ssh root@172.16.1.12 /sbin/ifconfig eth0
root@172.16.1.12's' password: #这里就需要我们手工输入密码。
eth0 Link encap:Ethernet HWaddr 00:0C:29:44:7E:84
inet addr:172.16.1.12 Bcast:172.16.1.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:fe44:7e84/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
...
下面我们通过Expect脚本来实现上述操作的自动交互。
[root@WEB-A1 ~]# mkdir -p /server/scripts
[root@WEB-A1 ~]# cd /server/scripts/
[root@WEB-A1 scripts]# vim ex-ssh.exp
#!/usr/bin/expect
spawn ssh root@172.16.1.12 /sbin/ifconfig eth0
set timeout 60
expect "*password:"
send "123456\n"
expect eof
exit
--------------------------
[root@WEB-A1 scripts]# chmod 700 ex-ssh.exp
[root@WEB-A1 scripts]# ./ex-ssh.exp
spawn ssh root@172.16.1.12 /sbin/ifconfig eth0
root@172.16.1.12's' password: #这里不再需要手动输入密码,而是自动完成这一步。
eth0 Link encap:Ethernet HWaddr 00:0C:29:44:7E:84
inet addr:172.16.1.12 Bcast:172.16.1.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:fe44:7e84/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
...
可以看到,Expect脚本中提供的密码为明文,但是通过将脚本权限设置为700,还是能够确保安全性的。
Expect的基本工作流程如下图所示:
Expect中最关键的四个命令是send,expect,spawn,interact。
send
:用于向进程发送字符串
expect
:从进程接收字符串
spawn
:启动新的进程
interact
:允许用户交互
spawn命令是Expect的初始命令,它用于开启一个进程,之后所有Expect脚本中涉及到的操作指令都是在与该进程进行交互。如果没有spawn语句,整个脚本将无法执行。指令的使用方法如下:
#在spawn指令后面直接加上要启动的进程、命令等信息,除此之外,还可以使用选项。
spawn ssh root@172.16.1.12 /sbin/ifconfig eth0
使用方法:expect 表达式 动作 表达式 动作 ...
expect命令和send命令正好相反,expect通常是用来等待一个进程的反馈。也就是说用于等候(或者说是接收)一个相匹配内容的输出,一旦匹配上就会执行expect后面的动作或者指令。其最常用的语法是来自Tcl语言的“模式-动作”。
该命令接受几个特有的参数,用得最多的就是“-re”,表示使用正则表达式的方式进行匹配。例如:
spawn ssh root@172.16.1.12
expect "password:" {send "123456\r"} #单一分支模式语法。
------------------------
#多分支模式语法:匹配到任意一个字符串时,执行相应的输出。
expect {
"hi" {send "You said hi\n"}
"hello" {send "Hello yourself\n"}
"bye" {send "That was unexpected\n"}
}
从上面的示例可以看出,expect是依附于spawn指令的,当执行spawn命令后面所接的指令后,expect就会匹配命令执行后的相应关键字,如果匹配到关键字就会执行其后面包含在大括号中的动作。当然,匹配语句以及后面的动作可以分两行来书写,这样的话就不需要使用大括号了,而实际完成的功能是一样的,只是写法不同而已。
#可以将上面示例中的一行分作两行来写:
expect "password:"
send "123456\r"
expect命令还有一种用法,它可以在一个expect匹配中多次匹配关键字,并给出处理动作,只需要将关键字放在一个大括号中就可以了,这其中还要使用“exp_continue”。
spawn ssh root@172.16.1.13
expect {
"yes/no" { exp_send "yes\r"; exp_continue }
"password:" { exp_send "123456\r" }
}
从上面的介绍中,我们已经看到了send指令的使用,它是expect脚本中的动作指令,它还有一个别名:exp_send
,两者的作用完全一致,用于将指定的字符串参数发送给进程。send指令同时可以发送一些特殊符号,例如:
“\r”表示回车,还有一些其他的符号例如:“\n”表示换行,“\t”表示制表符等等,这些都与TCL中的特殊符号相同。
send指令还有几个可以使用的选项:
选项 | 说明 |
---|---|
-i |
指定spawn_id 这个选项用来向不同spawn_id 的进程发送命令,是进行多程序控制的关键参数。 |
-s |
s 代表slowly ,也就是控制发送的速度,这个选项使用的时候要与expect中的变量send_slow 相关联。 |
这个命令一般用在动作中,它被使用的条件比较苛刻,参考下面的示例:
#!/usr/bin/expect
spawn ssh -p22 root@172.16.1.13 /sbin/ifconfig eth0
set timeout 60
expect {
-timeout 5
"yes/no" { exp_send "yes\r";exp_continue }
"password:" { exp_send "123456\r" }
timeout { puts "expect was timeout by oldboy.";return }
}
expect eof
exit
在这个示例中,可以发现“exp_continue”命令的使用方法。首先它要处于一个expect命令中,然后它属于一种动作命令,完成的工作就是从头开始遍历,也就是说如果没有这个命令,匹配第一个关键字以后就会继续匹配第二个关键字,但有了这个命令后,匹配第一个关键字以后,第二次匹配仍然从第一个关键字开始。
一般我们如果连续匹配多个内容的时候,一般要使用
exp_continue
将其进行隔开。
该命令用来把后面的参数输出到标准输出中去,send、exp_send命令默认都是将参数输出到程序中去。使用起来就像这样:
send_user "Please input passwd: "
这个语句就可以在标准输出中打印指定的文本内容了。
#!/usr/bin/expect
if { $argc != 3 } {
send_user "usage: expect scp-expect.exp file host dir\n"
exit
}
#define var
set file [lindex $argv 0]
set host [lindex $argv 1]
set dir [lindex $argv 2]
set password "123456"
spawn scp -P22 -p $file root@$host:$dir
set timeout 60
expect {
-timeout 2
"yes/no" {send "yes\r";exp_continue}
"password" {send "$password\r"}
timeout {puts "expect connect timeout, please contact oldboy."; return}
}
expect eof
exit -onexit {
send_user "Oldboy say good bye to you!\n"
}
它的功能比较简单,就是直接退出脚本,但是你可以利用这个命令对脚本做一些扫尾工作,比如下面这样:
exit -onexit {
exec rm $tmpfile
send_user "Good bye\n"
}
expect中有许多有用的变量,它们的使用方法与TCL语言中的变量相同。
set 变量名 变量值 #设置变量的方法
puts $变量名 #读取变量的方法
set file [lindex $argv 0]
set host [lindex $argv 1]
set dir [lindex $argv 2]
set password "123456"
Expect中的特殊关键字用于匹配过程,代表某些特殊含义或状态,一般用于expect族命令中而不能在外面单独的使用。也可以理解为事件,使用上类似于:
expect eof {}
eof(end-of-file)关键字用于匹配结束符,比如文件的结束符、FTP传输停止等情况,在这个关键字后跟上动作来做进一步的控制,特别是FTP交互操作方面,它的作用很大,用一个示例来演示:
spawn ftp anonymous@10.0.0.122
expect {
"password:" {exp_send "who I'm I"}
eof {ftp connect close}
}
interact {}
timeout是expect中的一个重要变量,它是一个全局性的时间控制开关,你可以通过为这个变量赋值来规定整个expect操作的时间,注意这个变量是服务于全局的,它不会纠缠于某一条指令,即使该指令没有任何的错误,到时间仍然会激活这个变量,但这个时间到达以后除了激活一个开关之外不会做其他的事情,如何处理是脚本编写人员的事情,看看它的实际使用方法:
set timeout 60
spawn ssh root@172.16.1.13
expect "password:" {send "123456\n"}
expect timeout {puts "Expect was timeout"; return}
上面的处理中,首先将超时时间变量设置为60秒,当出现问题时程序可能会停止下来,只要到60秒,就会激活下面的timeout动作,这里是打印一个信息并停止了脚本的运行。当然你也可以做其他更多的事情。
在另一种expect格式中,我们还有一种设置timeout变量的方法,如下所示:
spawn ssh root@172.16.1.13
expect {
-timeout 60
-re "password:" {exp_send "123456\r"}
-re "TopsecOS#" { }
timeout {puts "Expect was timeout"; return}
}
通过在expect命令中加上一个小横杠,也可以设置超时时间。
在timeout变量中,设置为0表示立即超时,-1则表示永不超时。
#!/bin/sh
. /etc/init.d/functions
for ip in `cat iplist`
do
expect oldboy-6.exp /etc/hosts $ip /etc/hosts >/dev/null 2>&1
if [ $? -eq 0 ];then
action "$ip" /bin/true
else
action "$ip" /bin/false
fi
done
PBSACS01:/home/ac>cat shell/sftp_down.sh
#!/bin/bash
SFTP=/usr/bin/sftp
PASSWD='91861888123$'
DATE=`date -d "1 day ago" +"%Y%m%d"`
cd /home/ac/data/psm/pfddbc/
#expect -c "
/usr/bin/expect<<-EOF
set timeout -1 # 取消超时
spawn $SFTP -oport=22 91861888@211.147.64.196:in/${DATE}/IA502PAY-${DATE}.csv
expect "Password:"
send "91861888123$\n"
expect eof
EOF
自动telnet
#!/bin/bash
IP=144.112.33.225
PORT=8830
#expect -c "
/usr/bin/expect<<-EOF
spawn telnet -e a $IP $PORT
expect "Escape*"
send "a"
send "quit\n"
expect eof
EOF