EXPECT 用法

     expect是基于TCL的相对简单的一个免费脚本编程工具语言,用来实现自动和交互式任务进行通信,无需人的

手工干预,比如ssh\FTP等,这些程序正常情况都需要手工与它们进行交互,而使用EXPECT就可以模拟人手工交互的过程,实现自动的

和远程的程序交互,从而达到自动化的目的。

    EXPECT是一个用来实现自动交互功能的软件套件(EXPECT is a software suite for automating interactive tools)

    虽然,使用C、perl等一样可以实现这样的功能,而expect做的更加专业出色、简单、而且除支持unix/linux平台外,它还支持

windows平台,它就是为系统管理和软件测试方面的自动交互类需求而产生的。


2.expect程序工作流程

   expect的工作流程可以理解为,spawn启动进程-->expect期待关键字---->send向进进程发送字符--->退出结束。


3.安装EXPECT软件

   

[root@clientC ~]# yum install expect -y
Loaded plugins: fastestmirror, refresh-packagekit
Determining fastest mirrors
 * base: mirror.bit.edu.cn
 * extras: ftp.sjtu.edu.cn
 * updates: mirrors.hust.edu.cn
base                                                     | 3.7 kB     00:00
base/primary_db                                          | 4.6 MB     00:00
[root@clientC ~]# rpm -qa | grep expect
expect-5.44.1.15-5.el6_4.x86_64
[root@clientC ~]# mkdir /scirts
[root@clientC ~]# cd /scirts/
[root@clientC scirts]# vi xp-excpec.exp
[root@clientC scirts]# chmod 700 xp-excpec.exp
[root@clientC scirts]# cat xp-excpec.exp
#!/usr/bin/expect
spawn ssh -p22 root@192.168.20.5 /sbin/ifconfig eth0
set timeout 60
expect "*password:"
send "ww123123\n"
expect eof
exit
[root@clientC scirts]# ./xp-excpec.exp
spawn ssh -p22 root@192.168.20.5 /sbin/ifconfig eth0
root@192.168.20.5's password:
eth0      Link encap:Ethernet  HWaddr 00:0C:29:D8:5D:8C
          inet addr:192.168.20.5  Bcast:192.168.20.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fed8:5d8c/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:9392 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2518 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:952097 (929.7 KiB)  TX bytes:461897 (451.0 KiB)


【EXPECT语法】

   Expect中的 命令是最重要的部分了,命令的使用语法如下;

命令{选项} 参数

    a.1.【spawn】 

        spawn命令是expect的初始命令,它用于启动一个进程,之后所有expect操作都在这个进程中进行,如果

没有spawn语句,整个expect就无法执行了,spawn使用方法如下;

  spawn ssh root@192.168.2.5

  在spawn命令后面,直接加上要启动的进程、命令等信息,除此之外,spawn还支持其他选项如;

   -open  启动文件进程,具体说明省略。

   -ignore 忽略某些信号,具体说明省略。

  a.2 【expect 使用方法:】

      expect 表达式 动作 表达式 动作。。。。。。。

    expect 命令用于等候一个相匹配内容的输出,一旦匹配上就执行expect后面的动作或命令,这个命令接受几个

特有参数,用的最多的就是-re,表示使用正则表达式的方式匹配,使用起来就像这样;

spawn ssh root@192.168.20.5
expect "password:"{send "ww123123\r"}

从上面的例子可以看出。expect是依附与spawn命令的,当执行ssh命令后,expect就匹配命令执行后的关键字:password:,

如果匹配到关键字就会执行后面包含在{}括号中的exp_send动作,匹配以及动作可以放在二行,这样就不需要使用{}括号了,

就像下面这样,实际完成的功能与上面是一样的;

spawn ssh root@192.168.20.5
expect "password:"
send "xiaoping\r"

expect 命令还有一种用法,它可以在一个expect匹配中多次匹配关键字,并给出处理动作,只需要将关键字放在一个大括号

就可以了,当然还有exp_continue.


spawn ssh root@192.168.20.5
expect {
  "yes/no"{exp_send "yes\r";exp_continue}
   "*password:" {exp_send "123123\r"}
}

a.3【exp_send和send】

    在上面的介绍中,我们已经看到exp_send命令的使用,exp_send命令是expect中的动作命令,它还有一个完成同样工作的

同胞:send,exp_send命令可以发送一些特殊符号,我们看到了\r(回车),还有一些其他的比如:\n(换行)、\t(制表符)等等

这些都是与TCL中的特殊符号相同。

spawn  ssh root@192.168.20.5
expect {
     "yes/no" {send "yes\r";exp_continue}
      "*password:" {exp_send "123456\r"}
}

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

   -i 指定spawn_id,这个参数用来向不同spawn_id的进程发送命令,是进行多程序控制的关键参数。

   -s s代表slowly,也就是控制发送的速度,这个参数使用的时候要与expect中的变量send_slow相关联。

a.4【exp_continue】

   这个命令一般用在动作中,它被使用的条件比较苛刻,看看下面的例子;

#!/usr/bin/expect
spawn ssh -p22 root@192.168.20.5 /sbin/ifconfig eth0
set timeout 60
expect {
      -timeout 1
        "yes/no" { exp_send "yes\r";exp_continue }
        "*password:" {exp_send "123456\r"}
          timeout {puts "expect was timeout by xiaoping.";return}
}
expect eof 
exit

    在这个例子中,可以发现exp_continue命令的使用方法,首先它要处于一个expect命令中,然后它属于一种动作命令

完成的工作就是从头开始偏历,也就是说如果没有这个命令,匹配第一个关键字以后就会继续匹配第二关键字,但有了这个

命令后,匹配第一个关键字以后,第二次匹配依然从第一个关键字开始。


A.5【send_user】

    send_user 命令用来把后面的参数输出到标准输出中去,默人的send、exp_send 命令都是将参数输出到程序中去的,

用起来就像这样;

     

send_user "please input passwd:"
这个语句就可以在标准输出中打印please input passwd:字符了。
[root@~]vi xp_3.exp
#!/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 "123123"
#spawn scp /etc/hosts root@192.168.20.1:/etc/hosts
spawn scp -p22 $file root@$host:$dir
expect {
    "yes/no" {send "yes\r";exp_continue}
     "*password" {send "$password\r"}
      timeout {puts "expect connect timeout,pls contact xp";return}
}
expect eof
  
exit -onexit {
    send_user "xiaoping say good bye to you!\n"
}
#expect xiaoping.exp


【Expect 变量】

 expect中有很多有用的变量,它们的使用方法与TCL语言中的变量相同,比如:

 set 变量名 变量值   #设置变量的方法

 puts $变量名        #读取变量的方法

#define var
set file [lindex $argv 0]
set host [lindex $argv 1]
set dir  [lindex $argv 2]
set password "123123123"

【Expect 关键字】

  expect中的特殊关键字用于匹配过程,代表某些特殊含义或状态,一般用于expect族命令中而不能在外面单独使用,

也可以理解为事件,使用上类似于:

    expect eof { }

7.1 eof 

   eof (end-of-file)关键字用于匹配结束符,比如文件的结束符,TFP传输停止等情况,在这个关键字后跟上动作来做

进一步的控制,特别是FTP交互操作方面,它的作用很大,用一个例子看看:

 spawn FTP root@192.168.20.5 
 expect {
    "password:" {exp_send "who am I"}
     eof {ftp connet close} 
}
interact {}

7.2 【timeout】

   timeout 是expect中的一个重要变量,它是一个全局的时间控制开关,你可以通过为这个变量赋值来规定整个expectc操作的

时间,注意这个变量是服务与expect全局的,它不会纠缠某一条命令,即使命令没有任何错误,到时间依然会激活这个变量,但

这个时间到达以后除了激活一个开关之外不会做其他的事情,如何处理脚本编写人员的事情,看看它的实际使用方法:

 

set timeout 60
  spawn ssh root@192.168.20.5
  expect "password:" {send "word\r"}
  expect timeout {puts "Expect was timeout";return}

上面的处理中。首先将timeout变量设置为60秒,当出现问题的时候程序可能会停止下来,只要到60秒,就会激活下面的timeout

动作,这里我打印一个信息并且停止了脚本的运行,你可以做更多的其他事情,看自己的意思。


在另外一种expect格式中我们还有一种设置timeout变量的方法,看看下面的例子;

   

spawn  ssh root@192.168.20.5
   expect {  
      -timeout 60
     -re "password:" {exp_send "word\r"}
     -re "Topsecos#"  { }
     timeout {puts "Expect was timeout";return}   
}

在expect命令中间加上一个小横杆,也可以设置timeout变量

timeout 变量中,设置为0表示立即超时,-1则表示永久不超时。

expect {
    -timeout 20
     "yes/no" {send "yes\r";exo_continue}
      "*password" {send "$password\r"}
       timeout {puts "expect connet timeout ,pls contact xp";return}
}

expect的命令行参数参考了c语言的,与bash shell有点不一样。其中,$argc为命令行参数的个数,$argv0为脚本名字本身,$argv为命令行参数。

[lrange $argv 0 0]表示第1个参数,[lrange $argv 0 4]为第一个到第五个参数。与c语言不一样的地方在于,$argv不包含脚本名字本身。

【实战1】批量分发文件

[root@clientC scirts]# cat fenfa_hosts.sh
#!/bin/sh
. /etc/init.d/functions   #调用fuctions函数
for ip in `cat iplist`
do
expect xp-scp.exp /etc/hosts $ip /xp  >/dev/null 2>&1  #可以修改别的拷贝文件
if [ $? = 0 ];then
action "$ip" /bin/true
else
action "$ip" /bin/false
fi
done
[root@clientC scirts]# cat xp-scp.exp
#!/urs/bin/expect  -f
if { $argc != 3 } {
 send_user "usage: expect xp-scp.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 "ww123123"
spawn scp -P22 -p $file roor@$host:$dir
set timeout 10
expect {
   -timeout 2
    "yes/no"  {send "yes\r" ;exp_continue}
  "*password:" {send "$password\r"}
  timeout {puts "expect connect timeout,pls contact xp"; retur}
}
expect eof
exit
[root@clientC scirts]#

【实战2】:使用expect实战批量分发ssh密钥文件

[root@clientC ~]# ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/root/.ssh/id_dsa):
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_dsa.
Your public key has been saved in /root/.ssh/id_dsa.pub.
The key fingerprint is:
2a:e1:3a:1a:b1:ec:eb:95:e5:16:d7:bb:7a:e2:88:87 root@clientC
The key's randomart p_w_picpath is:
+--[ DSA 1024]----+
|                 |
|                 |
|                 |
|        .        |
|.   .o .S.       |
|.o .+.o.  .      |
|o. o+o.  .       |
|..oEoo.. ..      |
|o=oo...o+.       |
+-----------------+
[root@clientC ~]# cd .ssh/
[root@clientC .ssh]# ls -a
.  ..  id_dsa  id_dsa.pub
[root@clientC .ssh]# grep \.ssh /etc/ss
ssh/ ssl/
[root@clientC .ssh]# grep \.ssh /etc/ssh/ssh
ssh_config            ssh_host_dsa_key      ssh_host_key          ssh_host_rsa_key
sshd_config           ssh_host_dsa_key.pub  ssh_host_key.pub      ssh_host_rsa_key.pub
[root@clientC .ssh]# grep \.ssh /etc/ssh/sshd_config
#       $OpenBSD: sshd_config,v 1.80 2008/07/02 02:24:18 djm Exp $
# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.
# This sshd was compiled with PATH=/usr/local/bin:/bin:/usr/bin
# The strategy used for options in the default sshd_config shipped with
[root@clientC .ssh]# cp id_dsa.pub  id_dsa.pub.back
[root@clientC .ssh]# mv id_dsa.pub .ssh/authorized_keys
mv: cannot move `id_dsa.pub' to `.ssh/authorized_keys': No such file or directory
[root@clientC .ssh]# ^C
[root@clientC .ssh]# mv id_dsa.pub authorized_keys
[root@clientC .ssh]# ls -a
.  ..  authorized_keys  id_dsa  id_dsa.pub.back
[root@clientC .ssh]# mv id_dsa
id_dsa           id_dsa.pub.back
[root@clientC .ssh]# mv id_dsa /xp/
[root@clientC .ssh]# ls -a
.  ..  authorized_keys  id_dsa.pub.back
[root@clientC .ssh]# cd ..
[root@clientC ~]# scp -P22 -p .ssh/ root@192.168.20.5:~/
The authenticity of host '192.168.20.5 (192.168.20.5)' can't be established.
RSA key fingerprint is c1:28:b4:c3:f6:3d:85:bf:b2:df:59:17:d5:9f:65:2e.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.20.5' (RSA) to the list of known hosts.
root@192.168.20.5's password:
.ssh: not a regular file
[root@clientC ~]# scp -P22 -p .ssh/ root@192.168.20.5:~/
root@192.168.20.5's password:
.ssh: not a regular file
[root@clientC ~]# scp -P22 -p -r .ssh/ root@192.168.20.5:~/
root@192.168.20.5's password:
id_dsa.pub.back                                             100%  602     0.6KB/s   00:00
authorized_keys                                             100%  602     0.6KB/s   00:00
known_hosts                                                 100%  394     0.4KB/s   00:00
[root@clientC ~]# ^C
#HostKey /etc/ssh/ssh_host_key
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_dsa_key
#AuthorizedKeysFile     .ssh/authorized_keys
# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
# Change to yes if you don't trust ~/.ssh/known_hosts for
#PidFile /var/run/sshd.pid
Subsystem       sftp    /usr/libexec/openssh/sftp-server
[root@clientC .ssh]# grep \AuthorizedKeysFile  /etc/ssh/sshd_config
#AuthorizedKeysFile     .ssh/authorized_keys
[root@clientC ~]# mkdri /server/scripts -p
[root@clientC scripts]# cat fenfa_sshkey.sh
#!/bin/sh
. /etc/init.d/functions
for ip in `cat iplist`
do
expect ex-ssh.exp  ~/.ssh/  $ip ~/.ssh/  >/dev/null 2>&1
if [ $? = 0 ];then
action "$ip" /bin/true
else
action "$ip" /bin/false
fi
done
[root@clientC scripts]#
特别提示:如果是禁止了root远程连接,那么就使用普通用户加sudo的方式在结合expect大量分发。
expect可以大量分发查询机器状态
【实战3】
[root@clientC scripts]# cat ex-free.exp
#!/urs/bin/expect
if { $argc != 3 } {
 send_user "usage: expect xp-scp.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 -r $file roor@$host:$dir
spawn ssh -P22 root@$file $host $dir
set timeout 10
expect {
  -timeout 10
   "yes/no"  {send "yes\r" ;exp_continue}
  "*password:" {send "$password\r"}
  timeout {puts "expect connect timeout,pls contact xp";return}
}
expect eof
exit
[root@clientC scripts]# cat piliang.sh
#!/bin/bash
for ip in `cat iplist`
do
expect ex-free.exp $ip free -m
done
[root@clientC scripts]# cat iplist
192.168.20.5
192.168.20.10

【测试】

[root@clientC scripts]# sh piliang.sh
spawn ssh -P22 root@192.168.20.5 free -m
root@192.168.20.5's password:
             total       used       free     shared    buffers     cached
Mem:           199        184         15          0         57         18
-/+ buffers/cache:        107         92
Swap:         1055          0       1055
spawn ssh -P22 root@192.168.20.10 free -m
ssh: connect to host 192.168.20.10 port 22: No route to host
expect: spawn id exp4 not open
    while executing
"expect eof"
    (file "ex-free.exp" line 20)
[root@clientC scripts]#




~