[关闭]
@cdmonkey 2016-12-12T09:34:28.000000Z 字数 5780 阅读 1130

Expect

基础文档


http://blog.chinaunix.net/uid-25324849-id-3191069.html

一、简介

Expect是基于Tcl的相对简单的一个免费的脚本编程工具语言,用来实现自动和交互式任务程序进行通信,无需人工进行干预。例如SSH、FTP等,这些程序正常情况下都需要手工与它们进行交互,而使用Expect就可以模拟手工交互的过程来实现自动的交互。

我们通过Shell可以实现简单的控制流功能,如:循环、判断等。但是对于需要交互的场合则必须通过人工来干预,有时候我们可能会需要实现自动和交互程序进行交互的功能,而Expect就使用来实现这种功能的工具。Expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。

二、安装Expect

Expect的安装非常的简单,通过yum进行安装即可:

  1. [root@WEB-A1 ~]# yum install -y expect
  2. [root@WEB-A1 ~]# rpm -qa|grep expect
  3. expect-5.44.1.15-5.el6_4.x86_64

我们先举一个小的示例,如果我们通过ssh指令连接到远程主机,一般情况下需要我们手动输入用户密码。

  1. [root@WEB-A1 ~]# ssh root@172.16.1.12 /sbin/ifconfig eth0
  2. root@172.16.1.12's' password: #这里就需要我们手工输入密码。
  3. eth0 Link encap:Ethernet HWaddr 00:0C:29:44:7E:84
  4. inet addr:172.16.1.12 Bcast:172.16.1.255 Mask:255.255.255.0
  5. inet6 addr: fe80::20c:29ff:fe44:7e84/64 Scope:Link
  6. UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
  7. ...

下面我们通过Expect脚本来实现上述操作的自动交互。

  1. [root@WEB-A1 ~]# mkdir -p /server/scripts
  2. [root@WEB-A1 ~]# cd /server/scripts/
  3. [root@WEB-A1 scripts]# vim ex-ssh.exp
  4. #!/usr/bin/expect
  5. spawn ssh root@172.16.1.12 /sbin/ifconfig eth0
  6. set timeout 60
  7. expect "*password:"
  8. send "123456\n"
  9. expect eof
  10. exit
  11. --------------------------
  12. [root@WEB-A1 scripts]# chmod 700 ex-ssh.exp
  13. [root@WEB-A1 scripts]# ./ex-ssh.exp
  14. spawn ssh root@172.16.1.12 /sbin/ifconfig eth0
  15. root@172.16.1.12's' password: #这里不再需要手动输入密码,而是自动完成这一步。
  16. eth0 Link encap:Ethernet HWaddr 00:0C:29:44:7E:84
  17. inet addr:172.16.1.12 Bcast:172.16.1.255 Mask:255.255.255.0
  18. inet6 addr: fe80::20c:29ff:fe44:7e84/64 Scope:Link
  19. UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
  20. ...

可以看到,Expect脚本中提供的密码为明文,但是通过将脚本权限设置为700,还是能够确保安全性的。

三、语法说明

Expect的基本工作流程如下图所示:

此处输入图片的描述

四个命令

Expect中最关键的四个命令是send,expect,spawn,interact。

send:用于向进程发送字符串
expect:从进程接收字符串
spawn:启动新的进程
interact:允许用户交互

1. spawn

spawn命令是Expect的初始命令,它用于开启一个进程,之后所有Expect脚本中涉及到的操作指令都是在与该进程进行交互。如果没有spawn语句,整个脚本将无法执行。指令的使用方法如下:

  1. #在spawn指令后面直接加上要启动的进程、命令等信息,除此之外,还可以使用选项。
  2. spawn ssh root@172.16.1.12 /sbin/ifconfig eth0

2. expect

使用方法:expect 表达式 动作 表达式 动作 ...

expect命令和send命令正好相反,expect通常是用来等待一个进程的反馈。也就是说用于等候(或者说是接收)一个相匹配内容的输出,一旦匹配上就会执行expect后面的动作或者指令。其最常用的语法是来自Tcl语言的“模式-动作”。

此处输入图片的描述

该命令接受几个特有的参数,用得最多的就是“-re”,表示使用正则表达式的方式进行匹配。例如:

  1. spawn ssh root@172.16.1.12
  2. expect "password:" {send "123456\r"} #单一分支模式语法。
  3. ------------------------
  4. #多分支模式语法:匹配到任意一个字符串时,执行相应的输出。
  5. expect {
  6. "hi" {send "You said hi\n"}
  7. "hello" {send "Hello yourself\n"}
  8. "bye" {send "That was unexpected\n"}
  9. }

从上面的示例可以看出,expect是依附于spawn指令的,当执行spawn命令后面所接的指令后,expect就会匹配命令执行后的相应关键字,如果匹配到关键字就会执行其后面包含在大括号中的动作。当然,匹配语句以及后面的动作可以分两行来书写,这样的话就不需要使用大括号了,而实际完成的功能是一样的,只是写法不同而已。

  1. #可以将上面示例中的一行分作两行来写:
  2. expect "password:"
  3. send "123456\r"

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

  1. spawn ssh root@172.16.1.13
  2. expect {
  3. "yes/no" { exp_send "yes\r"; exp_continue }
  4. "password:" { exp_send "123456\r" }
  5. }

3. exp_send & send

从上面的介绍中,我们已经看到了send指令的使用,它是expect脚本中的动作指令,它还有一个别名:exp_send,两者的作用完全一致,用于将指定的字符串参数发送给进程。send指令同时可以发送一些特殊符号,例如:

“\r”表示回车,还有一些其他的符号例如:“\n”表示换行,“\t”表示制表符等等,这些都与TCL中的特殊符号相同。

send指令还有几个可以使用的选项:

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

4. exp_continue

这个命令一般用在动作中,它被使用的条件比较苛刻,参考下面的示例:

  1. #!/usr/bin/expect
  2. spawn ssh -p22 root@172.16.1.13 /sbin/ifconfig eth0
  3. set timeout 60
  4. expect {
  5. -timeout 5
  6. "yes/no" { exp_send "yes\r";exp_continue }
  7. "password:" { exp_send "123456\r" }
  8. timeout { puts "expect was timeout by oldboy.";return }
  9. }
  10. expect eof
  11. exit

在这个示例中,可以发现“exp_continue”命令的使用方法。首先它要处于一个expect命令中,然后它属于一种动作命令,完成的工作就是从头开始遍历,也就是说如果没有这个命令,匹配第一个关键字以后就会继续匹配第二个关键字,但有了这个命令后,匹配第一个关键字以后,第二次匹配仍然从第一个关键字开始。

一般我们如果连续匹配多个内容的时候,一般要使用exp_continue将其进行隔开。

5. send_user

该命令用来把后面的参数输出到标准输出中去,send、exp_send命令默认都是将参数输出到程序中去。使用起来就像这样:

  1. send_user "Please input passwd: "

这个语句就可以在标准输出中打印指定的文本内容了。

  1. #!/usr/bin/expect
  2. if { $argc != 3 } {
  3. send_user "usage: expect scp-expect.exp file host dir\n"
  4. exit
  5. }
  6. #define var
  7. set file [lindex $argv 0]
  8. set host [lindex $argv 1]
  9. set dir [lindex $argv 2]
  10. set password "123456"
  11. spawn scp -P22 -p $file root@$host:$dir
  12. set timeout 60
  13. expect {
  14. -timeout 2
  15. "yes/no" {send "yes\r";exp_continue}
  16. "password" {send "$password\r"}
  17. timeout {puts "expect connect timeout, please contact oldboy."; return}
  18. }
  19. expect eof
  20. exit -onexit {
  21. send_user "Oldboy say good bye to you!\n"
  22. }

6. exit

它的功能比较简单,就是直接退出脚本,但是你可以利用这个命令对脚本做一些扫尾工作,比如下面这样:

  1. exit -onexit {
  2. exec rm $tmpfile
  3. send_user "Good bye\n"
  4. }

7. Expect变量

expect中有许多有用的变量,它们的使用方法与TCL语言中的变量相同。

set   变量名  变量值      #设置变量的方法
puts  $变量名             #读取变量的方法
  1. set file [lindex $argv 0]
  2. set host [lindex $argv 1]
  3. set dir [lindex $argv 2]
  4. set password "123456"

8. Expect关键字

Expect中的特殊关键字用于匹配过程,代表某些特殊含义或状态,一般用于expect族命令中而不能在外面单独的使用。也可以理解为事件,使用上类似于:

expect eof {}

8.1 eof

eof(end-of-file)关键字用于匹配结束符,比如文件的结束符、FTP传输停止等情况,在这个关键字后跟上动作来做进一步的控制,特别是FTP交互操作方面,它的作用很大,用一个示例来演示:

  1. spawn ftp anonymous@10.0.0.122
  2. expect {
  3. "password:" {exp_send "who I'm I"}
  4. eof {ftp connect close}
  5. }
  6. interact {}

8.2 timeout

timeout是expect中的一个重要变量,它是一个全局性的时间控制开关,你可以通过为这个变量赋值来规定整个expect操作的时间,注意这个变量是服务于全局的,它不会纠缠于某一条指令,即使该指令没有任何的错误,到时间仍然会激活这个变量,但这个时间到达以后除了激活一个开关之外不会做其他的事情,如何处理是脚本编写人员的事情,看看它的实际使用方法:

  1. set timeout 60
  2. spawn ssh root@172.16.1.13
  3. expect "password:" {send "123456\n"}
  4. expect timeout {puts "Expect was timeout"; return}

上面的处理中,首先将超时时间变量设置为60秒,当出现问题时程序可能会停止下来,只要到60秒,就会激活下面的timeout动作,这里是打印一个信息并停止了脚本的运行。当然你也可以做其他更多的事情。

在另一种expect格式中,我们还有一种设置timeout变量的方法,如下所示:

  1. spawn ssh root@172.16.1.13
  2. expect {
  3. -timeout 60
  4. -re "password:" {exp_send "123456\r"}
  5. -re "TopsecOS#" { }
  6. timeout {puts "Expect was timeout"; return}
  7. }

通过在expect命令中加上一个小横杠,也可以设置超时时间。

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

四、生产场景实战

1. 批量分发hosts文件

  1. #!/bin/sh
  2. . /etc/init.d/functions
  3. for ip in `cat iplist`
  4. do
  5. expect oldboy-6.exp /etc/hosts $ip /etc/hosts >/dev/null 2>&1
  6. if [ $? -eq 0 ];then
  7. action "$ip" /bin/true
  8. else
  9. action "$ip" /bin/false
  10. fi
  11. done

2. 批量分发SSH秘钥文件

嵌入Shell脚本

  1. PBSACS01:/home/ac>cat shell/sftp_down.sh
  2. #!/bin/bash
  3. SFTP=/usr/bin/sftp
  4. PASSWD='91861888123$'
  5. DATE=`date -d "1 day ago" +"%Y%m%d"`
  6. cd /home/ac/data/psm/pfddbc/
  7. #expect -c "
  8. /usr/bin/expect<<-EOF
  9. set timeout -1 # 取消超时
  10. spawn $SFTP -oport=22 91861888@211.147.64.196:in/${DATE}/IA502PAY-${DATE}.csv
  11. expect "Password:"
  12. send "91861888123$\n"
  13. expect eof
  14. EOF

自动telnet

  1. #!/bin/bash
  2. IP=144.112.33.225
  3. PORT=8830
  4. #expect -c "
  5. /usr/bin/expect<<-EOF
  6. spawn telnet -e a $IP $PORT
  7. expect "Escape*"
  8. send "a"
  9. send "quit\n"
  10. expect eof
  11. EOF
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注