shell脚本——expect的综合使用(包含应用案例)

Expect概述

  • Expect是建立在tcl基础上的一个工具,Expect 是用来进行自动化控制和测试的工具。主要解决shell脚本中不可交互的问题。对于大规模的linux 运维很有帮助
  • 在linux运维和开发中,我们经常需要远程登录服务器进行操作,登录的过程是一个交互的过程,可能会需要输入yes/no password等信息。为了模拟这种输入,可以使用Expect脚本

使用expect之前需要先安装
yum –y intall expect

基本命令

  • send:向进程发送字符串,用于模拟用户的输入
    该命令不能自动回车换行,一般要加 \r(回车)

  • expect:expect的一个内部命令,判断上次输出结果里是否包含指定的字符串,如果有则立即返回,否则就等待超时时间后返回。只能捕捉由spawn启动的进程的输出

  • spawn:启动进程,并跟踪后续交互信息

  • interact:执行完成后保持交互状态,把控制权交给控制台

  • Timeout:指定超时时间,过期则继续执行后续指令

  • exp_continue:允许expect继续向下执行指令

  • send_user:回显命令,相当于echo

  • $argv 参数数组:Expect脚本可以接受从bash传递的参数.可以使用[lindex $argv n]获得,n从0开始,分别表示第一个,第二个,第三个…参数

Expect脚本必须以interact或expect eof结束,执行自动化任务通常expect eof就够了

应用案例

磁盘自动分区脚本

#!/bin/bash

#安装expect
rpm -q expect &>/dev/null
if [ $? -ne 0 ];then
	yum install -y expect >/dev/null
	echo "expect安装成功"
fi

#设置挂载硬盘的参数
fdisk -l |egrep '^Disk /dev/sd.*'
read -p "请输入需要挂载得硬盘路径: " disk_new
partitions="${disk_new}1"

#expect交互脚本
/usr/bin/expect << EOF
set timeout 30
spawn bash -c "fdisk $disk_new"

expect "Command*" {send "n\r"} 
expect "*p*" {send "p\r"}
expect "*1*" {send "\r"}
expect "First*" {send "\r"}
expect "Last*" {send "\r"}
expect "*help*" {send "wq\r"} 

expect eof
interact	
EOF

#格式化磁盘
mkfs -t xfs $partitions


#挂载
read -p "输入需要挂载得路径:" target_dir
if [ ! -d $target_dir ]
then
	mkdir -p $target_dir
fi
echo "$partitions            $target_dir                xfs       defaults              0 0" >> /etc/fstab

#重载fstab配置
mount -a
df -Th

远程ssh另一台主机

需求1:A远程登录到server上什么都不做

#!/usr/bin/expect
# 开启一个程序
spawn ssh [email protected]
# 捕获相关内容
expect {
    
    
        "(yes/no)?" {
    
     send "yes\r";exp_continue }
        "password:" {
    
     send "123456\r" }
}
interact  # interact 这个写在最后代表交互,如果不写执行完动作就会退出

脚本执行方式:
# ./expect.sh
# /shell/expect.sh
# expect -f expect.sh

1)定义变量
#!/usr/bin/expect
set ip 192.168.188.188
set pass 123
set timeout 5
spawn ssh root@$ip
expect {
    
    
    "yes/no" {
    
     send "yes\r";exp_continue }
    "password:" {
    
     send "$pass\r" }
}
interact


2)使用位置参数
#!/usr/bin/expect
set ip [ lindex $argv 0 ]
set pass [ lindex $argv 1 ]
set timeout 5
spawn ssh root@$ip
expect {
    
    
    "yes/no" {
    
     send "yes\r";exp_continue }
    "password:" {
    
     send "$pass\r" }
}
interact

需求2:A远程登录到server上操作

#!/usr/bin/expect
set ip 192.168.188.188
set pass 123
set timeout 5
spawn ssh root@$ip
expect {
    
    
    "yes/no" {
    
     send "yes\r";exp_continue }
    "password:" {
    
     send "$pass\r" }
}

expect "#"
send "rm -rf /tmp/*\r"		# 删除临时文件
send "touch /tmp/file{1..3}\r"	# 创建临时文件
send "date\r"
send "exit\r"
expect eof

需求3:shell脚本和expect结合使用,在多台服务器上创建1个用户

[root@server shell04]# cat ip.txt 
192.168.188.188 123
192.168.188.186 123


1. 循环  useradd username
2. 登录远程主机——>ssh——>从ip.txt文件里获取IP和密码分别赋值给两个变量
3. 使用expect程序来解决交互问题


#!/bin/bash
# 循环在指定的服务器上创建用户和文件
while read ip pass
do
    /usr/bin/expect <<-EOF &>/dev/null
    spawn ssh root@$ip
    expect {
    
    
    "yes/no" {
    
     send "yes\r";exp_continue }
    "password:" {
    
     send "$pass\r" }
    }
    expect "#" {
    
     send "useradd maomao;rm -rf /tmp/*;exit\r" }
    expect eof
    EOF
echo "$ip服务器用户创建完毕"
done < ip.txt

自动连接ftp下载文件

#!/bin/bash
#自动连接ftp服务器然后下载pub下面的test.txt
#by stanZ 2021-1-27
if [ $# -eq 0 ];then
        echo "用法:`basename $0` ftp服务器ip地址"
        exit 1
fi
pgrep firewalld &>/dev/null
if [ $? -eq 0 ];then
        systemctl stop firewalld &>/dev/null
fi

if [[ $(getenforce) == Enforcing ]];then
        setenforce 0
        sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config &>/dev/null
fi

rpm -q expect &>/dev/null
if [ $? -ne 0 ];then
        [ $UID -eq 0 ] && yum install -y expect &>/dev/null || echo "你没有权限安装expect"
fi

{
    
    
        /usr/bin/expect <<-EOF 
        set timeout 5   
        spawn ftp $1
        expect  {
    
    
                "):" {
    
     send "ftp\r";exp_continue }
                "Password:" {
    
     send "\r" }
        }
        expect "ftp>" 
        send "cd pub\r" 
        send "get test.txt\r" 
        send "bye\r" 
        expect eof
        EOF
}&>/dev/null
wait
echo "test.txt文件下载完毕"
                               

综合案例

写一个脚本,将跳板机上yunwei用户的公钥推送到局域网内可以ping通的所有机器上
说明:主机和密码文件已经提供

192.168.188.188:123

192.168.188.186:123

步骤分析

  • 跳板机上的yunwei用户生成秘钥对
    判断账号是否存在 (id yunwei)
    判断该用户是否有密钥对文件 [ -f xxx ]
  • 判断expect程序是否安装
  • 判断局域网内主机是否ping通
    循环判断 for while
    循环体do…done ping 主机 如果ping通 调用expect程序自动应答推送公钥
  • 测试验证是否免密登录成功
  • 检查服务器上ssh服务端口号
  • 把公钥推送成功的主机的信息保存到文件
  • 关闭防火墙和selinux
  • 日志记录
  • 推送公钥需要自动应答expect

这里要实现功能 要切换成yunwei账户 必须写两个脚本文件
第一个脚本由root执行
第二个脚本由运维执行

#!/bin/bash
#ssh自动上传公钥,并且能远程控制
#by stanZ      2021-1-11
# 判断防火墙是否关闭
pgrep firewalld &>/dev/null
if [ $? -eq 0 ];then
        systemctl stop firewalld &>/dev/null	# 关闭防火墙
fi

# 关闭selinux安全中心
if [[ $(getenforce) = Enforcing ]];then
        setenforce 0
        sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config &>/dev/null
fi


{
    
    
id yunwei &>/dev/null	# 判断yunwei账户是否存在 不存在则创建
[ $? -ne 0 ] && useradd yunwei && echo 123|passwd --stdin yunwei
# 给yunwei账户sudo授权 取消注释
sed -ri 's/^#(.*required.*)/\1/' /etc/pam.d/su
sed -ri 's/^#(.*NOPASSWD)/\1/' /etc/sudoers
# 加入wheel组拥有权限
gpasswd -a yunwei wheel
} >/dev/null

# 判断expect是否安装
rpm -q expect &>/dev/null
if [ $? -ne 0 ];then
        yum install -y expect >/dev/null
        echo "expect安装成功"
fi

su - yunwei

补充:

[ ! -f /root/.ssh/id_rsa.pub ] && ssh-keygen -P '' -f /root/.ssh/id_rsa

ssh-keygen -P'' 	设定密码为空

 -f /root/.ssh/id_rsa 指定密钥位置
 
这样就不需要交互操作
#!/bin/bash
#ssh自动上传公钥,并且能远程控制
#by  stanZ     2021-1-11
# 判断私钥是否创建 如果没创建则使用免交互的命令
if [ ! -f /home/yunwei/.ssh/id_rsa ];then
        ssh-keygen -P '' -f ~/.ssh/id_rsa &>/dev/null
fi

while read ip pass
do
{
    
    
        ping -c1 $ip 
        if [ $? -eq 0 ];then
                echo $ip >> /home/yunwei/ip_up.txt
                /usr/bin/expect <<-EOF
                set timeout 10
                spawn ssh-copy-id root@$ip
                expect  "(yes/no)" {
    
     send "yes\r";exp_continue }
                expect  "password:" {
    
     send "$pass\r" }
                        expect eof
                EOF
                else
                        echo $ip >> /home/yunwei/ip_down.txt
                fi
}&>/dev/null
done < /home/ip.txt
wait
echo "公钥推送完毕,准备测试"
remote_ip=`head -1 /home/yunwei/ip_up.txt`
ssh root@$remote_ip hostname &>/dev/null
test $? -eq 0 && echo "公钥成功推送,并且能远程连接"

猜你喜欢

转载自blog.csdn.net/Cantevenl/article/details/115271301