(转)expect命令脚本语言介绍及生产实践
原文:http://www.fblinux.com/?p=526
Expect介绍
expect是一个用来实现自动交互功能的软件套件,是用来实现自动和交互式任务程序进行通信,无需人的手工干预。比如SSH、FTP等,这些程序正常情况下都需要手工和他们交互,而使用expect就可以模拟人手工交互的过程,实现自动化运维的目的。
总结一句话就是:expect就是为系统管理的自动交互类需求而产生的。
本文将介绍expect的基本语法,最后会有两个典型的交互式生产实例,分别是:
1、 ssh文件批量分发
2、 openvpn帐号自动创建
环境介绍
Expect服务器:IP:192.168.10.2 系统:Centos 6.8 64位
Client 服务器:IP:192.168.10.5 系统:Centos 6.8 64位
Expect安装
|
1
|
yum -y install expect |
Expect简单应用
在我们ssh连接服务器的情况下,如果没有把自己的公钥复制对目标主机的.ssh/authorized_keys文件下,我们是需要输入密码才可以连接的,但是在ssh脚本中如何实现自己输入密码?这个问题我们使用expect就可以解决,看如下操作:
直接使用ssh连接主机需要输入密码:
|
1
2
3
4
5
6
7
8
9
10
|
[root@ansible ~]# ssh 192.168.10.5 /sbin/ifconfig eth0root@192.168.10.5's password:eth0 Link encap:Ethernet HWaddr 00:16:3E:03:78:60 inet addr:192.168.10.5 Bcast:192.168.10.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:273007 errors:0 dropped:0 overruns:0 frame:0 TX packets:374106 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:93143657 (88.8 MiB) TX bytes:91464671 (87.2 MiB) Interrupt:160 |
这是一个写好的expect脚本
|
1
2
3
4
5
6
7
8
9
10
11
12
|
[root@ansible ~]# cat expect.expect#!/usr/bin/expectspawn ssh 192.168.10.5 /sbin/ifconfig eth0set timeout 60expect { -timeout 5 "yes/no" { exp_send "yes\r" } "*password:" { exp_send "passtest\r" } timeout {puts "expect was timeout by fblinux."; return}}expect eofexit |
使用expect命令执行这个脚本,我们可以看到没有提示我们输入密码就可以在目标主机执行命令
|
1
2
3
4
5
6
7
8
9
10
11
|
[root@ansible ~]# expect expect.expectspawn ssh 192.168.10.5 /sbin/ifconfig eth0root@192.168.10.5's password:eth0 Link encap:Ethernet HWaddr 00:16:3E:03:78:60 inet addr:192.168.10.5 Bcast:192.168.10.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:273073 errors:0 dropped:0 overruns:0 frame:0 TX packets:374223 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:93162147 (88.8 MiB) TX bytes:91491805 (87.2 MiB) Interrupt:160 |
至于为什么?在看完下面的expect语法介绍后,你就明白了
Expect语法
Spawn
Spawn命令是expect的初始命令,他用于启动一个进程,之后所有expect都在这个进程中进行,如果没有spawn语句,整个expect就没有办法进行了,spawn使用方法如下:
|
1
|
spawn ssh 192.168.10.5 /sbin/ifconfig eth0 |
在spawn命令后面,直接加上要启动的进程、命令信息,除此之外,spawn还支持其他选项如:-open:启动文件进程
-ignore:忽略某些信号
Expect
Expect表达式,动作 表达式 动作……
Expect命令用于等候一个相匹配内容的输出,一旦匹配上就执行expect后面的动作或命令,这个命令接收几个特有的参数,用的最多的就是-re,表示使用正则表达式的方式匹配,使用案例如下
|
1
2
|
spawn ssh -p22 root@192.168.100.2 /sbin/ifconfigexpect "*password:" { send "passtest\r"} |
从上面的例子可以看出,expect是依附与spawn命令的,当执行ssh命令后,expect就匹配命令执行后的关键字:password:,如果匹配到了关键字就执行包含在{}括号中的send或exp_send动作,匹配以及动作可以放在二行,这样就不需要使用{}括号了,就像下面这样,实际完成的功能与上面是一样的。
|
1
2
3
|
spawn ssh -p22 root@192.168.100.2 /sbin/ifconfigexpect "*password:" send "passtest\r" |
exp_send和send
在上面的介绍中,我们已经看到了exp_send命令的使用,exp_send是expect中的动作命令,可以发送一些特殊符号,\r表示回车,\n换行、\t制表符等等,这些都与TCP中的特殊符号相同。
|
1
2
3
|
spawn ssh -p22 root@192.168.100.2 /sbin/ifconfigexpect "*password:" send "passtest\n" |
send命令还有几个可用的参数:-i:指定spawn_id,这个参数用来向不同的spawn_id的进程发送命令,是进行多程序控制的关键参数。
-s:s代表slowly,也就是控制发送的速度,这个参数使用的时候要与expect中的变量send_slow相关联。
Exp_continue
这个命令一般用在动作中,它被使用的条件比较,看看下面的例子:
|
1
2
3
4
5
6
7
8
9
10
11
|
#!/usr/bin/expectspawn ssh 192.168.10.5 /sbin/ifconfig eth0set timeout 60expect { -timeout 5 "yes/no" { exp_send "yes\r";exp_continue } "*password:" { exp_send "passtest\r" } timeout {puts "expect was timeout by fblinux."; return}}expect eofexit |
在这个例子中,可以发现exp_continue命令的使用方法,首先它要处于一个expect命令中,然后它属于一种动作命令,完成的工作就是从头开始遍历,也就是说如果没有这个命令,匹配第一个关键字以后就会继续匹配第二个关键字,但有了这个命令后,匹配第一个关键字以后,第二次匹配仍然从第一个关键字开始。
Send_user
Send_user命令用来把后面的参数输出到标准输出中去,默认的send、exp_send命令都是将参数输出到程序中去:
例子:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#!/usr/bin/expectif { $argc != 3 } { #判断:如果执行脚本传入的参数少于3个,则在终端输入下面send_user中的内容 send_user "usage: expect scp-expect.exp file host dir\n" exit}#define varset file [lindex $argv 0]set host [lindex $argv 1]set dir [lindex $argv 2]set password "passtest"spawn scp $file root@$host:$direxpect { "yes/no" {send "yes\r";exp_continue} "*password" {send "$password\r"}}expect eofexit |
如果执行脚本的时候参数少于三个,输入如下内容
|
1
2
|
[root@ansible ~]# expect expect.expectusage: expect scp-expect.exp file host dir |
如果执行脚本的时候传入正确参数则执行结果如下
|
1
2
3
4
|
[root@ansible ~]# expect expect.expect /etc/passwd 192.168.10.5 /root/spawn scp /etc/passwd root@192.168.10.5:/root/root@192.168.10.5's password:passwd 100% 1321 1.3KB/s 00:00 |
exit
exit命令功能很简单,就是直接退出脚本,但是你可以利用这个命令对脚本做一些扫尾工作,比如下面这样:
|
1
2
3
4
|
exit -onexit { exec rm $tmpfile #删除临时文件 send_user "Oldboy say good bye to you!\n" #终端输出字符} |
Expect变量
Expect中有很多有用的变量,他们的使用方法如下,比如:
- set 变量名 变量值 #设置变量
- puts $变量名 #读取变量
|
1
2
3
4
|
set file [lindex $argv 0]set host [lindex $argv 1]set dir [lindex $argv 2]set password "123456" |
Timeout
Timeout是expect中的一个重要变量,他是一个全局性的时间控制开关,你可以通过为这个变量赋值来规定整个expect的操作的时间,注意这个变量是全局的,他不会纠缠于某一条命令,即使命令没有任何错误,到时间任然会激活这个变量,但这个时间到达以后除了激活一个开关之外不会做其他的事情,如何处理是脚本编写人员的事情。
使用案例如下:
|
1
2
3
4
|
set timeout 60spawn ssh root@192.168.10.5expect "password:" {send "word\r"}expect timeout {puts "expect was timeout";return} |
上面的处理中,首先将timeout变量设置为60秒,当出现问题的时候程序可能会停止下来,只要到60秒,就会激活下面的timeout动作。
在另一种expect格式中,我们还有一种设置timeout变量的方法,看看下面的例子。
|
1
2
3
4
5
6
7
|
spawn ssh root@192.168.10.5expect { -timeout 5 "yes/no" { exp_send "yes\r" } "*password:" { exp_send "redhat\r" } timeout {puts "expect was timeout by fblinux."; return}} |
生产场景expect使用案例
ssh分发
场景说明:比如你新安装了一批服务器,你需要通过ansible来管理服务器(ansible是通过ssh来认证的),这个时候如果每台服务器都使用ssh-copy-id来交互就比较麻烦了,因为每台服务器都需要输入密码,这种场景使用expect实现是极好的。
分发前的准备工作
1、 初始化服务器的初始密码必须是一样的,这个应该放在运维装机规范中
2、 把自己的公钥文件命名为authorized_keys放到一个目录中,因为脚本是直接拷贝.ssh目录
|
1
2
|
[root@ansible ~]# mkdir /data/.ssh/[root@ansible ~]# cp .ssh/id_rsa.pub /data/.ssh/authorized_keys |
准备完成之后就可以写脚本了,我写好的脚本如下:
expect脚本:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
[root@ansible ~]# cat /shell/expect.exp#!/usr/bin/expectif { $argc != 3 } { send_user "usage: expect scp-expect.exp file host dir\n" exit}#define varset file [lindex $argv 0]set host [lindex $argv 1]set dir [lindex $argv 2]spawn scp -P22 -r -p $file root@$host:$direxpect { "yes/no" {send "yes\r";exp_continue} "*password" {send "passtest\r"}}expect eofexit |
shell脚本:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
[root@ansible ~]# cat /shell/ssh_init.ssh#!/bin/bash. /etc/init.d/functionshost="192.168.10.5" #多个主机使用空格分隔,也可以使用两个变量网段+IP的形式,然后循环IP地址for ip in $host;do expect /shell/expect.exp /data/.ssh/ $segment$ip /root/ >>/dev/null if [ $? -eq 0 ];then action "$segment$ip" /bin/true else action "$segment$ip" /bin/false fidone |
使用示例:
|
1
2
|
[root@ansible ~]# sh /shell/ssh_init.ssh192.168.10.5 [ OK ] |
执行完成之后,我们就可以直接登录到目标主机,而无须输入密码
Openvpn用户自动创建脚本
场景说明:我们每次创建openvpn用户的时候都需要有一大堆交互如输入国家、省份、城市、组织、邮件等等信息,特别麻烦。我现在想写一个脚本,只需要输入员工姓名和邮箱,就会自动把生产的密钥信息,还有client安装包,以及使用教程发送给员工,让员工按照教程操作,我写的脚本如下:
注意:使用脚本需要配置邮件发送环境,不然创建成功后,无法自动发送证书文件和使用教程给员工。邮件发送参考我另外一篇博文Linux下使用mutt,msmtp发信
expect脚本:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
[root@openvpn ~]# cat /shell/vpn_expect.expect#!/usr/bin/expect -fif $argc<1 { puts stderr "Usage: $argv0 need argv.\n" exit 1}set vpnuser [lindex $argv 0]set path /usr/local/openvpn/easy-rsa/2.0/spawn $path/build-key $vpnuserexpect "*"send "\r"expect "*"send "\r"expect "*"send "\r"expect "*"send "\r"expect "*"send "\r"expect "*"send "\r"expect "*"send "\r"expect "*"send "\r"expect "*"send "\r"expect "*"send "\r"expect "*"send "y\r"expect "*"send "y\r"expect eofexit |
openvpn帐号开通shell脚本:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
[root@openvpn ~]# cat /shell/create_vpnuser.sh#!/bin/bash# 设置相关变量work_dir=/usr/local/openvpn/easy-rsa/2.0tmp_dir=/data/tmp/openvpnmail_content=/shell/mail.txtclient_config=client.ovpn# 使用帮助help(){ echo '添加vpn用户执行命令sh create_vpnuser.sh add' echo '删除vpn用户执行命令sh create_vpnuser.sh del'}# 判断用户是否存在add_user(){ # 交互输入用户名和邮箱 read -p "please input a user name:" name read -p "please input a user email:" email if [ -f $work_dir/keys/$name.crt ];then echo "新建vpn用户存在,请检查!" exit 1 else # 创建用户密钥 cd $work_dir && source ./vars /usr/bin/expect /shell/vpn_expect.expect $name if [ $? != 0 ];then echo "创建密钥失败" exit 10 fi # 密钥和配置文件打包 cd $work_dir/keys/ cp $name.* ca.crt $tmp_dir if [ $? != 0 ];then echo "复制密钥失败" exit 20 fi sed -i s@vpnclient@$name@g $tmp_dir/$client_config cd $tmp_dir tar zcf $name.tar.gz $name.* ca.crt $client_config openvpn-2.2.2-install.exe # 发送邮件给员工 sed -i s@vpnclient@$name@g $mail_content cat $mail_content | mutt -s "VPN帐号开通" $email -a $tmp_dir/$name.tar.gz sed -i s@$name@vpnclient@g $tmp_dir/$client_config sed -i s@$name@vpnclient@g $mail_content fi}del_user(){ # 交互输入用户名 read -p "please input a user name:" name if [ -f $work_dir/keys/$name.crt ];then cd $work_dir && source ./vars && ./revoke-full $name else echo "删除vpn用户不存在,请检查" fi}main(){ case $1 in add) add_user; ;; del) del_user; ;; *) help; esac}main $1 |
发送邮件的内容:
|
1
2
3
4
|
[root@openvpn ~]# cat /shell/mail.txthi vpnclient: 你的vpn帐号已经开通,使用方法见wiki连接;有问题及时与我联系。 这里放上你们公司wiki中的openvpn安装教程 |
转载请注明:西门飞冰的博客-专注于Linux运维 » expect命令脚本语言介绍及生产实践
(转)expect命令脚本语言介绍及生产实践的更多相关文章
- Windbg命令脚本流程控制语句详解
在Windbg命令脚本一文里,我们介绍了命令脚本语言的的组成要素,在本文里将对语句进行展开的讲解.这些语句主要是流程控制的语句,比如我们常见的条件分子和循环语句等. ; (命令分隔符) 分号(:)字符 ...
- Linux 下 expect 脚本语言中交互处理常用命令
Linux 下 expect 脚本语言中交互处理常用命令 1. #!/usr/bin/expect 告诉操作系统脚本里的代码使用那一个 shell 来执行.这里的 expect 其实和 Linux 下 ...
- Shell脚本学习之expect命令
转载:http://blog.csdn.net/leexide/article/details/17485451 目录(?)[-] 一概述 二expect的安装 一Tcl 安装 二expect 安装 ...
- Cocos2d-x 脚本语言Lua介绍
Cocos2d-x 脚本语言Lua介绍 本篇博客记录Lua学习.学习来自eoe论坛,Lua语言开发Cocos2d-x游戏入门视频教程,猛戳下面地址: http://www.eoeandroid.com ...
- 使用Lua脚本语言开发出高扩展性的系统,AgileEAS.NET SOA中间件Lua脚本引擎介绍
一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...
- 使用expect脚本语言写一键发布服务(代码发布、所有服务重启)
互联网服务有很多台服务,但是在上线的时候需要将这些服务版本都更新与个个都重启,下面的脚本语言,就是一键发布服务~ 1.在/home/weihu/deploy/ 目录下建下publish .publis ...
- TCL脚本语言基础介绍
Tcl简介(一):Tcl 语法 Tcl 语法 Tcl是一种很通用的脚本语言,它几乎在所有的平台上都可以释运行,其强大的功能和简单精妙的语法会使你感到由衷的喜悦,这片文章对 Tcl有很好的描述和说明.如 ...
- shell脚本语言与linux命令的联系与区别
使用linux肯定是要会使用命令的,就算提供有用户界面,绝大部分功能还是要通过命令行去操作的.而shell脚本语言也是运行在linux上的脚本语言,对于服务器运维人员也是几乎必须要掌握的.而shell ...
- shell中使用expect命令进行远程执行命令脚本
expect是用来实现自动交互功能的工具之一,使用expect-send来实现交互过程. 注意: 1.脚本的执行方法与bash shell不一样,比如:expect example.sh 2.向一个脚 ...
随机推荐
- spring mvc注解@RequestMapping
1.url路径映射 基本功能 2.窄化请求映射 根路径+子路径 注意setViewName的路径. 3.限制http请求方法 get和 post 如果是get
- 如何解决Android开发中的【java.lang.unsatisfiedlinkerror findLibrary returned null.】 错误
将脉可寻的功能加入到自己的APP中时,需要在libs文件中添加.so文件和jar包 但是,加入.so文件后,仍然报错 在一番折腾之后,终于解决了,然而解决的方法很奇异- -. 在libs下新建一个ar ...
- C# 自带的.net类库 实现得到本机IP以及网关地址
今天需要用到一个功能,获取主机名和本机的IP 准备用API实现的,然后稍微查了一下,发现.net类库已经有了 就在System.Net命名空间中的DNS类中 GetHostName 获取本地计算机的主 ...
- memcached整理の缓存问题
声明:博客来源http://www.cnblogs.com/AloneSword/p/3931905.html,谢谢他的分享! 缓存穿透与缓存雪崩 缓存系统不得不考虑的另一个问题是缓存穿透与失效时的雪 ...
- centos 6.5下安装mysql
1.检测系统是否已经安装过mysql或其依赖,若已装过要先将其删除,否则第4步使用yum安装时会报错: 1 # yum list installed | grep mysql 2 mysql-libs ...
- Verilog MIPS32 CPU(一)-- PC寄存器
Verilog MIPS32 CPU(一)-- PC寄存器 Verilog MIPS32 CPU(二)-- Regfiles Verilog MIPS32 CPU(三)-- ALU Verilog M ...
- MIPS rev.1 指令参数
由于MIPS各个版本之间的操作数会变,如果出现无法识别的情况 需要根据官方的MIPS instruction手册逐条核对,此处的版本为Rev.1 //******MIPS-55*********// ...
- ML.NET 0.9 版本发布---.net下的机器学习引擎
欢迎来到 2019年!在过去的9个月里, 我们一直在为ML.NET添加新的特征和改进相关功能.在提交1.0版本之前,我们将专注于包的整体稳定性并对API进行不断优化, 扩大测试的覆盖面并对开发文档进行 ...
- MongoDB复制集与Raft协议异同点分析
此文已由作者温正湖授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 一.日志复制流程: a.raft leader节点在接收client请求后,先将请求写到日志中,再将日志通过 ...
- 二十三、MongoDb 数据库介绍、安装、启动和连接(非关系型数据库)
1.数据库和文件的主要区别 1. 数据库有数据库表.行和列的概念,让我们存储操作数据更方便2. 数据库提供了非常方便的接口,可以让 nodejs.php java .net 很方便的实现增加修改删除功 ...