一、概述
Expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。Expect的作者Don
Libes在1990年开始编写Expect时对Expect做有如下定义:Expect是一个用来实现自动交互功能的软件套件。通过expect系统管理员可以创建脚本用来实现对命令或程序提供输入,而这些命令和程序是期望从终端(terminal)得到输入,一般来说这些输入都需要手工输入进行的。Expect则可以根据程序的提示模拟标准输入提供给程序需要的输入来实现交互程序执行。甚至可以实现实现简单的BBS聊天机器人。
我们通过Shell可以实现简单的控制流功能,如:循环、判断等。但是对于需要交互的场合则必须通过人工来干预,有时候我们可能会需要实现与SSH、FTP服务器等进行免交互的自动连接功能,而Expect正是用来实现这种功能的工具。
Expect是不断发展的,随着时间的流逝,其功能越来越强大,已经成为系统管理员的的一个强大助手。Expect需要Tcl编程语言的支持,所以要在系统上运行Expect必须首先安装Tcl。
通过ssh远程操控主机时交互解决方式:
1. 通过ssh的密钥对
2. 通过sshpass工具提交密码
3. 通过expect工具提交密码
二、安装expect
[root@localhost ~]# rpm -q expect
[root@localhost ~]# yum -y install expect
三、如何使用expect
在使用expect程序前,我们首先简单说下expect在常规使用中的工作流程:
首先expect的内部命令spawn启动指定进程-->expect获取期待的关键字-->内部命令send向指定进程发送响应内容-->进程执行完成后,退出expect程序。
通过上面expect的工作流程,我们可以认识到,expect的内置命令:spawnexpectsend等非常重要,所以下面我们先来了解下它们的使用。
3-1、spawn命令
spawn作用:启动新的产生交互的进程
spawn命令的语法:
spawn [选项] [需要执行的shell命令或程序等]
下面我们以修改一个已存在账户的密码为例,举例如下:
[root@localhost ~]# useradd tom
[root@localhost ~]# passwd tom
更改用户 tom 的密码 。
新的 密码:
无效的密码: 密码未通过字典检查 - 过于简单化/系统化
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
基于expect交互界面做法
[root@localhost ~]# expect
expect1.1> spawn passwd tom
spawn passwd tom
109731
expect1.2> expect "新的 密码:"
更改用户 tom 的密码 。
新的密码: send "123456\r"
""valid command name "s
while executing
"s\r""
expect1.4> expect "新的 密码:"
^C[root@localhost ~]# expect
expect1.1> spawn passwd tom
spawn passwd tom
110137
expect1.2> expect "新的 密码:"
更改用户 tom 的密码 。
新的密码: expect1.3> send "123456\r"
expect1.4> expect "新的 密码:"
无效的密码: 密码少于 8 个字符
重新输入新的密码: expect1.5> send "123456\r"
expect1.6> expect eof
passwd:所有的身份验证令牌已经成功更新。
expect1.7> exit
3-2、expect命令
expect作用:获取从spawn命令执行的命令和程序后产生的交互信息。看看是否匹配,如果匹配,就开始执行expect进程接收字符串。
expect命令的语法:
expect [选项] 表达式 [动作]
选项:比如"-re"表示使用正则表达式来进行匹配。
案例:
[root@localhost ~]# expect
expect1.1> spawn passwd tom
spawn passwd tom
1437
expect1.2> expect "新的 密码:"
更改用户 tom 的密码 。
新的 密码:expect1.3> send "123456r"
expect1.4> expect "新的 密码:"
无效的密码: 密码少于 8 个字符
重新输入新的 密码:expect1.5> send "123456r"
expect1.6> expect eof
passwd:所有的身份验证令牌已经成功更新。
expect1.7> exit
3-3、send命令
send命令的主要作用是,在expect命令匹配完指定的字符后,发送指定的字符串给系统程序,在字符中可以支持部分特殊转义符,比如:n(回车)r(换行)t(制表符)等。
案例:
修改密码案例:
[root@localhost ~]# vim use_pwd.sh
#!/usr/bin/expect
# Filename : change_pass2.exp
spawn passwd tom
expect {
"新的 密码:" { send "123456\r"; exp_continue }
"新的 密码:" { send "123456\r" }
eof
}
[root@localhost ~]# chmod +x use_pwd.sh
[root@localhost ~]# ./use_pwd.sh
[root@localhost ~]# ./change_pass1.exp
spawn passwd tom
更改用户 tom 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
3-4、exp_continue命令
exp_continue命令的主要作用是,如果需要一次匹配多个字符串,那么多次匹配字符串并执行不同的动作中,可以让expect程序实现继续匹配的效果。
案例:
[root@localhost ~]# vim chang_pass2.exp
#!/usr/bin/expect
# Filename : change_pass2.exp
spawn passwd tom
expect {
"新的 密码:" { send "123456r"; exp_continue }
"新的 密码:" { send "123456r" }
eof
}
[root@localhost ~]# chmod +x chang_pass2.exp
[root@localhost ~]# ./chang_pass2.exp
spawn passwd tom
更改用户 tom 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
3-5、expect变量
3-5-1、普通变量
expect中定义普通变量的语法如下:
set 变量名 变量值
例如:
[root@localhost ~]# vim expect_var.exp
#!/usr/bin/expect
# Filename: expect_var.exp
set name "crushlinux"
set address "beijing"
puts $name
send_user "$name t $addressn"
[root@localhost ~]# chmod +x expect_var.exp
[root@localhost ~]# ./expect_var.exp
crushlinux
crushlinux beijing
3-6、expect中常用关键字
3-6-1 eof关7键字
eof是和spawn对应的,当spawn发送指令到终端执行起始会有一个eof,等指令在终端完毕后,在返回时eof被expect捕捉,就好比在shell中
cat >>file <<OEFrr content rr
EOF一样,在结束时也要有EOF,这样是对应的。因前面案例中已有举例,这里就不再举例说明。
Interact允许用户交互,由管理员结束进程。
3-6-2 timeout关键字
expect脚本我们都知道,首先spawn我们要执行的命令,然后就给出一堆expect的屏幕输出,如果输出匹配了我们的expect的正则匹配内容,我们就会send一个命令上去,模拟用户输入。
但是expect中等待命令的输出信息是有一个timeout的设定的,默认是10秒。这个特性是防止那些执行死机的命令的。一旦到了这个timeout,还是没有屏幕输出的话,expect脚本中下面的代码就会执行。或者我们在expect脚本中如果定义了timeout的响应代码的话,这些代码就会被执行。
解决这样的问题非常简单,最简单的办法就是在expect脚本的开头定义:
set timeout -1 -- 永久不超时
set timeout 0 -- 立即执行
set timeout XX -- 设定具体的timeout时间(秒),默认是10秒。
案例:
[root@localhost ~]# vim ssh_login.sh
#!/usr/bin/expect
spawn ssh root@192.168.79.150 ip a
expect {
"*yes/no* " { send "yes\r"; exp_continue }
"*password: " { send "q1w2e3@123!!!!!\r"; exp_continue }
}
[root@localhost ~]# ./ssh_login.sh
spawn ssh root@192.168.79.150 ip a
Authorized users only. All activities may be monitored and reported.
root@192.168.79.150's password:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:c7:e2:fd brd ff:ff:ff:ff:ff:ff
inet 192.168.79.150/24 brd 192.168.79.255 scope global dynamic noprefixroute ens160
valid_lft 1304sec preferred_lft 1304sec
inet6 fe80::20c:29ff:fec7:e2fd/64 scope link noprefixroute
valid_lft forever preferred_lft forever
四、Shell脚本调用expect 的方法
1. 在shell脚本中使用expect --c "..."可以在shell中调用expect编程语言;
[root@localhost ~]# vim ssh_login.sh
#!/bin/bash
for i in 192.168.19.150 192.168.79.150
do
expect <<EOF
spawn ssh root@$i ip a
expect {
"*yes/no* " { send "yes\r"; exp_continue }
"*password: " { send "q1w2e3@123!!!!!\r"; exp_continue }
}
EOF
done
[root@localhost ~]# ./ssh_login.sh
spawn ssh root@192.168.19.150 ip a
spawn ssh root@192.168.79.150 ip a
Authorized users only. All activities may be monitored and reported.
root@192.168.79.150's password:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:c7:e2:fd brd ff:ff:ff:ff:ff:ff
inet 192.168.79.150/24 brd 192.168.79.255 scope global dynamic noprefixroute ens160
valid_lft 1641sec preferred_lft 1641sec
inet6 fe80::20c:29ff:fec7:e2fd/64 scope link noprefixroute
valid_lft forever preferred_lft foreve
1220

被折叠的 条评论
为什么被折叠?



