本文主要讲解Shell脚本开发中Expect自动化交互式程序的应用实践
目录:
1.Expect介绍
2.安装Expect软件和简单实现交互功能
3.Expect程序自动交互的重要命令及实践
4.Expect程序变量介绍
5.Expect程序中的if条件语句
一:Expect介绍
1)什么是Expect
Expect是一个用来实现自动交互功能的软件套件,是基于TCL的脚本编程工具语言,方便学习,功能强大。
2)为什么要使用Expect
在现今的企业运维中,自动化运维已经成为运维的主流趋势,但是在很多情况下,执行系统命令或程序时,系统会以交互式的形式要求运维人员输入指定的字符串,之后才能继续执行命令。例如,为用户设置密码时,一般情况下就需要手工输入2次密码。
[root@aliyun shuzu]# passwd ming66 Changing password for user ming66. New password: BAD PASSWORD: it is WAY too short BAD PASSWORD: is too simple Retype new password: passwd: all authentication tokens updated successfully.
简单来说,Expect就是用来自动实现与交互式程序通信的,而无需管理员的手工干预。比如SSH、FTP远程连接等,正常情况下都需要手工与它们进行交互,而使用Expect就可以模拟手工交互的过程,实现自动与远端程序的交互,从而达到自动化运维的目的。
以下是Expect的自动交互工作流程简单说明,依次执行如下操作:
spawn启动指定进程——>expect获取期待的关键字——>send向指定进程发送指定字符——>进程执行完毕,退出结束
二:安装Expect软件和简单实现交互功能
准备好两台实验虚拟机,IP地址为192.168.1.7和192.168.1.8 并设置好yum源,安装Expect安装包
[root@aliyun ~]# yum install expect -y [root@aliyun ~]# ifconfig |grep 'inet addr:'|awk -F "[ :]" '{print $13}' 192.168.1.7 127.0.0.1 [root@aliyun ~]# ssh -p22 root@192.168.1.8 The authenticity of host '192.168.1.8 (192.168.1.8)' can't be established. RSA key fingerprint is c8:f2:9e:53:55:98:37:10:31:8f:1a:62:9a:58:c7:8e. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '192.168.1.8' (RSA) to the list of known hosts. root@192.168.1.8's password: Last login: Sat Jun 9 03:31:56 2018 from 192.168.1.11 [root@aliyun ~]# ifconfig |grep 'inet addr:' |awk -F "[ :]" '{print $13}' 192.168.1.8 127.0.0.1 [root@aliyun ~]# exit logout Connection to 192.168.1.8 closed. [root@aliyun ~]# ssh -p22 root@192.168.1.8 root@192.168.1.8's password: Last login: Sat Jun 9 03:37:29 2018 from 192.168.1.7
可以看到,每次都需要手工输入密码,才能执行ssh命令,否则无法执行
下面就用Expect的功能实现自动交互,发送密码并执行上诉ssh命令
[root@aliyun ~]# cat ywxi.exp #!/usr/bin/expect #脚本开头解析器,和shell类似,表示程序使用expect解析 spawn ssh root@192.168.1.8 uptime #执行ssh命令(注意开头必须要有spawn,否则无法实现交互) expect "*password" #利用expect获取执行上述ssh命令输出的字符串是否为期待的字符串*.password,这里的*是通配符 send "123456\n" #当获取到期待的字符串*password时,则发送123456密码给系统,\n为换行 expect eof #处理完毕后结束Expect
#执行expect脚本
[root@aliyun ~]# which expect /usr/bin/expect [root@aliyun ~]# expect ywxi.exp spawn ssh root@192.168.1.8 uptime root@192.168.1.8's password: 03:46:18 up 16 min, 3 users, load average: 0.00, 0.02, 0.06 [root@aliyun ~]# expect ywxi.exp spawn ssh root@192.168.1.8 uptime root@192.168.1.8's password: 03:46:30 up 16 min, 3 users, load average: 0.00, 0.02, 0.06
三:Expect程序自动交互的重要命令及实践
Expect程序中的命令是Expect的核心
1)spawn命令
在Expect自动交互程序执行的过程中,spawn命令是一开始就需要使用的命令,通过spawn执行一个命令或程序,之后所有的Expect操作都会在这个执行过的命令或程序进程中进行,包括自动交互功能,因此如果没有spawn命令,Expect程序将无法实现自动交互。所以使用spawn命令是Expect程序实现自动交互工作流程中的第一步,也是最关键的一步
apawn命令的语法为:
spawn [ 选项 ] [ 需要自动交互的命令或程序 ] 选项: -open 表示启动文件进程 -ignore 表示忽略某些信号
例如:
spawn ssh root@192.168.1.8 uptime
2)expect命令
expect命令的作用就是获取spawn命令执行后的信息,看看是否和其事先指定的相匹配,一旦匹配上指定的内容就执行expect后面的动作,expect命令也有一些选项,相对用得较多的是-re,表示使用正则表达式的方式来匹配。
expect命令的语法为:
expect 表达式 [ 动作 ]
例如:
spawn ssh root@192.168.1.8 uptime expect "*password"
注:上述命令不能直接在linux命令行中执行,需要放入expect脚本中执行
expect命令实践
范例1:执行ssh命令远程获取服务器负载值,并要求实现自动输入密码
法1:将expect和send放在一行 [root@aliyun ~]# cat ywxi.exp #!/usr/bin/expect spawn ssh root@192.168.1.8 uptime expect "*password" {send "123456\n"} expect eof 执行结果: [root@aliyun ~]# expect ywxi.exp spawn ssh root@192.168.1.8 uptime root@192.168.1.8's password: 04:06:30 up 36 min, 3 users, load average: 0.00, 0.00, 0.00 法2:将expect和send不放在一行 [root@aliyun ~]# cat ywxi.exp #!/usr/bin/expect spawn ssh root@192.168.1.8 uptime expect "*password" send "123456\n" expect eof 执行结果: [root@aliyun ~]# expect ywxi.exp spawn ssh root@192.168.1.8 uptime root@192.168.1.8's password: 04:07:52 up 37 min, 3 users, load average: 0.00, 0.00, 0.00
范例2:利用expect响应shell脚本中的多个read读入
脚本前准备: [root@aliyun pratice]# cat readex.sh #!/bin/bash read -p 'please input your username:' name read -p 'please input your password:' pass read -p 'please input your email:' email echo -n "you name is $name," echo -n "you password is $pass," echo -n "you email is $email." [root@aliyun pratice]# sh readex.sh please input your username:ywxi please input your password:123456 please input your email:1031664140@qq.com you name is ywxi,you password is 123456,you email is 1031664140@qq.com. expect脚本如下: [root@aliyun pratice]# cat expe.exp #/usr/bin/expect spawn /bin/sh readex.sh #这里在一个目录下,用的相对路径 expect { "username" {exp_send "ywxi\r";exp_continue} #若获取到的是username信息,则自动输入ywxi "*pass*" {send "123456\r";exp_continue} #若获取到的是*pass*信息,则自动输入123456 "*mail*" {exp_send "1031664140@qq.com\r"} #若获取到的是*mail*信息,则自动输入邮件地址 } expect eof 执行结果如下: [root@aliyun pratice]# expect expe.exp spawn /bin/sh readex.sh please input your username:ywxi please input your password:123456 please input your email:1031664140@qq.com you name is ywxi,you password is 123456,you email is 1031664140@qq.com.
3)send命令
exp_send和send命令的使用方法,这两个命令是expect中的动作命令,用法类似,即在expect命令匹配指定的字符串后,发送指定的字符串给系统,这些命令可以支持一些特殊转义符号,例如: \r表示回车、\n表示换行、\t表示制表符等。
send命令有几个可用的参数,具体如下:
-i : 指定spawn_id,用来向不同的spawn_id进程发送命令,是进行多程序控制的参数
-s: s代表slowly,即控制发送的速度,使用的时候要与expect中的变量send_slow相关联
4)exp_continue命令
这个命令一般处于expect命令中,属于一种动作命令,一般用在匹配多次字符串的动作中,从命令的拼写就可以看出命令的作用,即让Expect程序继续匹配的意思。
使用示例如下:
[root@aliyun pratice]# cat expe.exp #/usr/bin/expect spawn /bin/sh readex.sh expect { "username" {exp_send "ywxi\r";exp_continue} "*pass*" {exp_send "123456\r";exp_continue} "*mail*" {exp_send "1031664140@qq.com\r"} } expect eof
5)send_user命令
send_user命令可用来打印Expect脚本信息,类似shell里的echo命令,而默认的send、exp_send命令都是将字符串输出到Expect程序中去。
示例如下:
[root@aliyun pratice]# cat send.exp #!/usr/bin/expect send_user "I am ywxi.\n" #\n表示换行 send_user "I am linuxer,\t" #\t表示Tab键 [root@aliyun pratice]# expect send.exp I am ywxi. I am linuxer, [root@aliyun pratice]
四:Expect程序变量介绍
1)普通变量
Expect中的变量定义、使用方法与TCL语言中的变量基本相同
定义变量的基本语法如下:
set 变量名 变量值
示例如下:
set password “123456”
打印变量的基本语法如下:
puts $变量名
范例:定义及输出变量
[root@aliyun pratice]# cat bl.exp #!/usr/bin/expect set password "123456" puts $password send_user "$password\n" #send_user 也可以打印输出 [root@aliyun pratice]# expect bl.exp 123456 123456
2)特殊参数变量
在Expect里也有与shell脚本里的$0、$1、$#等类似的特殊参数变量,用于接收及控制Expect脚本传参。
在Expect中$argv表示参数数组,可以使用 [ index $argv n ] 接收Expect脚本传参,n从0开始,分别表示第一个 [ index $argv 0 ]参数、第二个 [ index $argv 1 ] 参数、第三个[ index $argv 2 ] 参数。
范例:定义及输出特殊参数变量
[root@aliyun pratice]# cat var.exp #!/usr/bin/expect set file [lindex $argv 0] set host [lindex $argv 1] set dir [lindex $argv 2] send_user "$file\t$host\t$dir\n" puts "$file\t$host\t$dir\n" 执行结果如下: [root@aliyun pratice]# expect var.exp ywxi 192.168.1.7 /etc ywxi 192.168.1.7 /etc ywxi 192.168.1.7 /etc
Expect接收参数的方式和bash脚本的方式有些区别,bash是通过$0...$n这种方式来接收的,而Expect是通过set<变量名称>[lindex $argv <param index>]来接收的,例如set file [lindex $argv 0]
除了基本的位置参数外,Expect也支持其他的特殊参数,例如:$argc表示传参的个数,$argv0表示脚本的名字。
范例:针对Expect脚本传参的个数及脚本名参数的实践
[root@aliyun pratice]# cat var.exp #!/usr/bin/expect set file [lindex $argv 0] set host [lindex $argv 1] set dir [lindex $argv 2] puts "$file\t$host\t$dir" puts $argc puts $argv0 执行结果如下: [root@aliyun pratice]# expect var.exp ywxi.txt 192.168.1.7 /opt/ ywxi.txt 192.168.1.7 /opt/ #这是脚本后面的三个参数 3 #这是参数的总个数 var.exp #这是脚本的名字
五:Expect程序中的if条件语句
Expect程序中if条件语句的基本语法为:
if {条件表达式} { 指令 } 或 if {条件表达式} { 指令 } else { 指令 } 说明:if关键字后面要有空格,else关键字前后都要有空格,{条件表达式}大括号里面靠近大括号处可以没有空格,将指令括起来的起始大括号"{"前要有空格。
范例如下:使用if语句判断脚本传参的个数,如果不符合则给予提示。
[root@aliyun pratice]# cat ifexp.exp #!/usr/bin/expect if { $argc != 3 } { send_user "usage: expect $argv0 file host dir\n" exit } set file [lindex $argv 0] set host [lindex $argv 1] set dir [lindex $argv 2] puts "$file\t$host\t$dir" [root@aliyun pratice]# expect ifexp.exp usage: expect ifexp.exp file host dir [root@aliyun pratice]# expect ifexp.exp 1 2 3 1 2 3