原文链接:https://blog.csdn.net/qq_41944882/article/details/105956317
一、概述
通过Shell可以实现简单的控制流功能,如:循环、判断等。但是对于需要交互的场合则必须通过人工来干预,有时候我们可能会需要实现和交互程序如telnet服务器等进行交互的功能。而Expect就使用来实现这种功能的工具。
Expect是一个免费的编程工具语言,用来实现自动与交互式任务进行通信,而无需人工干预。Expect是一个用来实现自动交互功能的软件套件 。系统管理员可以用它来创建脚本以实现对命令或程序提供输入,而这些命令和程序是期望从终端(terminal)输入的,一般来说这些输入都需要手工输入。 Expect则可以根据程序的提示模拟标准输入提供给程序需要的输入来实现交互程序执行。甚至可以实现实现简单的BBS聊天机器人。Expect是不断发展的,随着时间的流逝,其功能越来越强大,已经成为系统管理员的的一个强大助手。Expect需要Tcl编程语言的支持,要在系统上运行Expect必须首先安装Tcl。
二、Expect工作原理
从最简单的层次来说,Expect的工作方式象一个通用化的Chat脚本工具。Chat脚本最早用于UUCP网络内,以用来实现计算机之间需要建立连接时进行特定的登录会话的自动化。
Chat脚本由一系列expect-send对组成:expect等待输出中输出特定的字符,通常是一个提示符,然后发送特定的响应。例如下面的 Chat脚本实现等待标准输出出现Login:字符串,然后发送somebody作为用户名;然后等待Password:提示符,并发出响应 sillyme。
引用:login: somebody pssword: sillyme
这个脚本用来实现一个登录过程,并用特定的用户名和密码实现登录,Expect最简单的脚本操作模式本质上和Chat脚本工作模式是一样的。
三、安装expect
1.yum源安装expect,使用yum源可以自动解决依赖
[root@test1 man]# yum -y install expect
1
2.源码包方式安装expect
(1)安装tcl软件包,因为expect依赖于tcl,所以得先解决依赖问题
这里安装8.4.19版本
[root@test1 mnt]# wget https://sourceforge.net/projects/tcl/files/Tcl/8.4.19/tcl8.4.19-src.tar.gz
1
解压缩并安装tcl
[root@test1 mnt]# tar -zxvf tcl8.4.19-src.tar.gz
[root@test1 mnt]# cd tcl8.4.19/unix && ./configure --prefix=/usr/tcl && make && make install
1
2
(2)下载expect并安装
这里下载expect5.45
[root@test1 mnt]# wget http://sourceforge.net/projects/expect/files/Expect/5.45/expect5.45.tar.gz
1
解压缩并安装exxpect
[root@test1 mnt]# tar -zxvf expect5.45.tar.gz
[root@test1 mnt]# cd expect5.45/ && ./configure --prefix=/usr/expect --with-tcl=/usr/tcl/lib --with-tclinclude=../tcl8.4.19/generic && make && make install
[root@test1 expect5.45]# ln -s /usr/tcl/bin/expect /usr/bin/expect
1
2
3
四、expect的语法
语法:expect [选项] [ -c cmds] [ [ -[f|b] ] cmdfile] [ args]
选项:-c:从命令行执行expect脚本,默认expect是交互地执行的 -d:可以输出输出调试信息
expect中相关命令:
spawn:启动新的进程
send:用于向进程发送字符串
expect:从进程接收字符串
interact:允许用户交互
exp_continue:匹配多个字符串在执行动作后加此命令
expect:最常用的语法(tcl语言:模式-动作)
set timeout 30:设置超时时间timeout为30s,expect命令阻塞超时时会自动往下继续执行。将timeout配置为-1时表示expect一直阻塞直到与期待的字符串匹配上才继续往下执行。超时时间timeout默认为10s。
[lindex $argv n]:可以在脚本中使用该命令获取在脚本执行时传入的第n个参数。这里argv为传入的参数,另外argv为传入的参数,另外argc表示传入参数的个数,$argv0表示脚本名字。另外我们也可以使用[lrange $argv sn en]命令获取第sn到第en个参数。
1
2
3
4
5
6
7
8
实用代码分析
通过一些常用的expect脚本来具体的说明如何使用expect来完成日常的一些工作
#!/usr/bin/expect //使用expect来解释该脚本
set timeout 30 //设置超时时间,单位为秒,默认情况下是10秒
set host "192.168.33.124" //设置变量
set username "root"
set pass "123456"
spawn ssh $username@$host //spawn:是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。它主要的功能是给ssh运行进程加个壳,用来传递交互指令
expect "*password*" {send "$pass\r"} //这里的expect也是expect的一个内部命令,这个命令的意思是判断上次输出结果里是否包含“password”的字符串,如果有则立即返回;否则就等待一段时间后返回,这里等待时长就是前面设置的30秒
interact //执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上
1
2
3
4
5
6
7
8
在上述的示例中,涉及到expect中一个非常重要的概念——模式-动作;即上述expect "*password*" {send "$password\r"}这句代码表达出来的含义。简单的说就是匹配到一个模式,就执行对应的动作;匹配到password字符串,就输入密码。你可能也会看到这样的代码:
expect {
"password" {
send "$pass\r"
exp_continue
}
eof
{
send "eof"
}
}
1
2
3
4
5
6
7
8
9
10
其中exp_continue表示循环式匹配,通常匹配之后都会退出语句,但如果有exp_continue则可以不断循环匹配,输入多条命令,简化写法。
很多时候,我们需要传递参数到脚本中,现在通过下面这段代码来看看如何在expect中使用参数:
#!/usr/bin/expect
if {$argc < 3} {
puts "Usage:cmd <host> <username> <password>"
exit 1
}
set timeout -1
set host [lindex $argv 0]
set username [lindex $argv 1]
set pass [lindex $argv 2]
spawn ssh $username@$host
expect "*password*" {send "$pass\r"}
interact
1
2
3
4
5
6
7
8
9
10
11
12
在expect中,$argc表示参数个数,而参数值存放在$argv中,比如取第一个参数就是[lindex $argv 0],依此类推。
五、expect应用实例
1.传参式ssh登陆指定主机
#!/usr/bin/expect -f
set ip [ lindex $argv 0 ] //接收第一个参数,并设置IP
set pass [ lindex $argv 1 ] //接收第二个参数,并设置密码
set timeout 10 //设置超时时间
spawn ssh root@$ip //发送ssh请求
expect { //返回信息匹配
"*yes/no" { send "yes\r"; exp_continue} //第一次ssh连接会提示yes/no,自动发送yes
"*password:" { send "$pass\r" } //出现密码提示,发送密码
}
interact //交互模式,用户会停留在远程服务器上面
1
2
3
4
5
6
7
8
9
10
2.利用expect批量ssh互信
编写hosts文本用于存放主机信息,格式如下:
编写脚本,如下所示:
#!/bin/bash
# 判断id_rsa密钥文件是否存在
if [ ! -f ~/.ssh/id_rsa ];then
ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa
else
echo "id_rsa has created ..."
fi
#分发到各个节点,这里分发到host文件中的主机中.
while read line
do
user=`echo $line | cut -d " " -f 2`
ip=`echo $line | cut -d " " -f 1`
passwd=`echo $line | cut -d " " -f 3`
expect <<EOF
set timeout 10
spawn ssh-copy-id $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$passwd\n" }
}
expect "password" { send "$passwd\n" }
EOF
done < hosts
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
由于是做实验,只在hosts文件里放了一台机器的信息,如果是很多太机器的话,可以把其他机器的信息按照相同的格式放入hosts文件中,运行该脚本的机器都可以免密ssh登陆其他机器,如果需要相互之间都能免密登陆,则每台机器都执行该脚本,可以结合saltsatck自动化运维工具来使每台机器都执行该脚本。
文章知识点与官方知识档案匹配,可进一步学习相关知识