一、使用说明
当我们执行ssh远程连接命令时,有时让输入用户密码,有时需要输入是否——yes/no继续建立连接,如果我们想要编写一个可以自动执行远程连接操作的脚本,就需要根据shell中具体的交互信息作出不同的回答,类似这种情况在编写脚本时就需要使用expect语句
二、使用方法
(1)、创造模拟环境
编写问题脚本,运行脚本时逐个输出提示语提示用户输入姓名、年龄和学科,读取用户输入的字符串并将其赋值给相应的变量,输出显示变量值的组合语句
问题脚本
read -p "What is your name: " NAME
read -p "How old are you: " AGE
read -p "Which subject do you like : " SUBJECT
echo $NAME is a $AGE old person which like $SUBJECT
执行效果
第一种
逐个输入回答问题
第二种方式
提前准备好输入,准备应答
(2)、expect的使用
expect语句来根据shell中具体的交互信息自动作出不同的回答
dnf install expect -y
安装完成后,当我们编写问题脚本(.sh结尾),脚本中需要用户多次输入内容回答问题时,可以编写相应的应答expect脚本(以.exp结尾)自动检测其监控执行的脚本(.sh)所提示的问题,根据问题的关键字匹配对应的回答给问题脚本。except和shell一样有自己运行的环境(#!/usr/bin/expect),应答expect脚本主要有以下四种编写方式:
1)、编写方式一
在 expect 环境中使用多个单独的内部命令expect,判断交互中输出的信息里是否包含某些关键字,如果有则立即根据关键字匹配对应的回答并将回答send发送给问题脚本
注意:
1、每个回答后的 \r 必须写,否则会卡住出错;
2、spwan监控执行脚本,传递交互指令;
3、脚本结束标志可以为xcept eof、interact,except eof表示结束当前except运行环境,interact表示保留当前except运行环境并退出;
vim answer.exp
///
#!/usr/bin/expect 运行的环境
spawn /root/question.sh 回答文件的位置
expect "name"
send "ck\r"
expect "old"
send "24\r"
expect "subject"
send "linux\r"
expect eof
执行后结果显示
自动匹配问题
2)、编写方式二
我们可以在一个expect模块中写入多个关键字,这样当expect脚本中回答数量多于问题脚本(.sh结尾)所提示的问题时,多出的回答不会导致匹配出错,这里exp_continue参数的功能是使同一expect模块中的匹配语句可以继续执行
注意模块内最后一个回答不用写exp_continue参数,否则会卡住出错
vim answer1.exp
///
#!/usr/bin/expect
spawn /root/question.sh
expect {
"name" { send "ck\r";exp_continue }
"color" { send "blue\r";exp_continue } 多写了一行,进行测试。
"old" { send "22\r";exp_continue }
"subject" { send "linux\r" }
}
expect eof
多写的一行不会影响匹配机制
3)、编写方法三
我们可以使用变量读取执行except脚本时脚本后用户输入的字符串,将其作为问题关键字对应匹配的回答,实现了动态应答功能,这里set timeout 数字表示应答出现问题出错时最多等待几秒就结束运行/
注意定义变量稍微有点不同,且最后一个一定要是能回答的
vim answer2.exp
/
#!/usr/bin/expect
set timeout 3
set NAME [ lindex $argv 0 ] $argv 0表示except脚本后跟的第一个字符,以此类推
set OLD [ lindex $argv 1 ]
set SUBJECT [ lindex $argv 2 ]
set COLOR [ lindex $argv 3 ] 多写一行
spawn /root/question.sh
expect {
"name" { send "$NAME\r";exp_continue }
"old" { send "$OLD\r";exp_continue }
"color" { send "$COLOR\r";exp_continue}
"subject" { send "$SUBJECT\r" }
}
expect eof
分别赋予变量NAME,OLD,SUBJECT,COLOR的值为ck,24,linux,blue结果如下
4)、编写方法四
当应答脚本需要和shell脚本一起运行的时候,需要同一在/bin/bash进行运行。此时可以采用编写方法三、将expect的相关命令使用EOF输入到except运行环境中运行,注意此时expect模块中的内容要顶格写,此时可以使用sh xxx.exp的方式运行expect脚本
注意运行环境为/bin/bash
vim answer2.exp
///
#!/bin/bash
/usr/bin/expect <<EOF
spawn /root/question.sh
expect {
"name" { send "$1\r";exp_continue }
"old" { send "$2\r";exp_continue }
"color" { send "$3\r" ;exp_continue} 多写了一行
"subject" { send "$4\r" } 这行一定要有应答,不然会出错
}
expect eof
EOF
用普通脚本的方式进行
三、练习
编写脚本host_list.sh检测172.25.254.190-172.25.254.201网络是否开启,如果网络正常则生成解析列表host_list,解析列表格式为:ip 主机名称
思路:使用for循环依次ping172.25.254.190-172.25.254.201网络,如果可以ping通则将网络ip和通过远程连接执行hostname命令得到的主机名保存在解析列表文件host_list中,这里要注意,当我们需要进行远程连接时,有时系统会提示请回答连接主机的密码,有时系统会提示请回答是否进行连接,所以我们需要编写一个expect自动应答函数,监控执行远程连接命令,根据提示的问题自动匹配回答
vim jiaoben.sh
/
#!/bin/bash
Autossh() 这里写了一个应答函数
{
/usr/bin/expect <<EOF
spawn ssh -l root $1 $2 在每次的for循环里面$1代表ip,$2代表询问hostanme
expect {
"yes/no" { send "yes\r;exp_continue" }
"password" { send "westos\r;exp_continue" }
}
expect eof
EOF
}
for ID in {190..201}
do
ping -c1 -w1 172.25.42.$ID &> /dev/null && {
echo -e "172.25.42.$ID\t `Autossh 172.25.42.$ID hostname|tail -n1`" >> /root/host_list
}
done
因为我们开启了一台172.25.42.200的主机,主机名字为p2,所以只记录了这台主机的信息。