Shell script-comprehensive use of expect (including application cases)

Expect overview

  • Expect is a tool based on tcl, and Expect is a tool for automated control and testing. Mainly solve the problem of non-interaction in shell scripts. Very helpful for large-scale linux operation and maintenance
  • In Linux operation and maintenance and development, we often need to remotely log in to the server for operations. The login process is an interactive process, and you may need to enter information such as yes/no password. In order to simulate this input, you can use the Expect script

Need to install before using expect
yum –y intall expect

Basic command

  • send: Send a string to the process to simulate the user's input.
    This command cannot automatically enter and wrap. Generally, you need to add\r (carriage return)

  • expect: An internal command of expect, which determines whether the specified string is included in the last output result, and returns immediately if there is, otherwise, it returns after waiting for the timeout period. Can only capture the output of the process started by spawn

  • spawn: Start the process and track subsequent interaction information

  • interact: After the execution is completed, keep the interactive state and transfer control to the console

  • Timeout: Specify the timeout period, and continue to execute subsequent instructions when it expires

  • exp_continue: Allow expectcarry onExecute down instructions

  • send_user: echo command, equivalent to echo

  • $argv parameter array: The Expect script can accept the parameters passed from bash. It can be obtained by using [lindex $argv n], n starts from 0, indicating the first, second, third...parameters respectively

Expect scripts must end with interact or expect eof, and expect eof is usually sufficient to perform automated tasks

Applications

Disk automatic partition script

#!/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

Remote ssh another host

Requirement 1: A remotely logs in to the server and does nothing

#!/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

Requirement 2: A remotely logs in to the server to operate

#!/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

Requirement 3: Combine shell script and expect to create a user on multiple servers

[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

Automatically connect to ftp to download files

#!/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文件下载完毕"
                               

Comprehensive case

Write a script to push the public key of the yunwei user on the springboard to all machines in the LAN that can be pinged.
Note: The host and password file have been provided

192.168.188.188:123

192.168.188.186:123

Step analysis

  • The yunwei user on the springboard generates a key pair to
    determine whether the account exists (id yunwei) to
    determine whether the user has a key pair file [-f xxx]
  • Determine whether the expect program is installed
  • Judge whether the host in the LAN is pingable or not.
    Cycle judgement for while
    loop body do...done ping. If the host is pinged, call the expect program and automatically respond and push the public key.
  • Test to verify whether the password-free login is successful
  • Check the ssh service port number on the server
  • Save the information of the host that successfully pushed the public key to a file
  • Turn off the firewall and selinux
  • Logging
  • Pushing the public key requires an automatic response to expect

To achieve the function, you must write two script files to switch to the yunwei account
. The first script is executed by root and the
second script is executed by operation and maintenance.

#!/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

supplement:

[ ! -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 "公钥成功推送,并且能远程连接"

Guess you like

Origin blog.csdn.net/Cantevenl/article/details/115271301