Expect 是一个强大的自动化交互工具,特别适合处理需要用户交互的命令行程序。
一、Expect 核心详解
1. Expect 基本流程
- spawn:启动目标程序
- expect:等待特定模式出现
- send:发送响应内容
- interact(可选):将控制权交还用户
2. Expect常用命令
spawn
spawn ssh user@host # 启动ssh连接
spawn ftp $ftp_host # 启动FTP连接
expect
expect {
"password:" {send "mypass\r"} # 匹配password:
"yes/no" {send "yes\r"; exp_continue} # 继续等待
timeout {exit 1} # 超时处理
eof {exit} # 程序结束
}
send
send "ls -l\r" # 发送命令
send "exit\r" # 发送命令
send -- "$var\r" # 发送变量,--防止解释为选项
二、Expect脚本案例
案例1:自动SSH登录v1.0
#!/usr/bin/expect
spawn ssh root@10.9.48.121
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "123\r" }
}
interact
案例2:自动SSH登录v2.0
#!/usr/bin/expect
set ip 10.9.48.121
set user root
set password 123
set timeout 5
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "$password\r" }
}
interact
案例3:自动SSH登录v3.0
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set timeout 5
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "$password\r" }
}
#interact
expect "#"
send "useradd alice\r"
send "pwd\r"
send "exit\r"
expect eof
[root@lichu ~]# ./expect_ssh_login_v3.sh 10.9.48.121 root 123
spawn ssh root@10.9.48.121
root@10.9.48.121's password:
Last login: Thu Aug 14 16:46:30 2025 from 10.9.48.236
[root@nfs-server ~]# useradd alice
[root@nfs-server ~]# pwd
/root
[root@nfs-server ~]# exit
logout
Connection to 10.9.48.121 closed.
案例4:自动远程复制文件
#!/usr/bin/expect
set ip [lindex $argv 0]
set password [lindex $argv 1]
set timeout 5
#spawn rsync -av /etc/hosts root@$ip:/tmp
spawn scp -r /etc/hosts root@$ip:/tmp
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "$password\r" };
}
expect eof
三、Expect 与 Shell 结合
1. 基本结合方式
方法1:在Shell中调用Expect脚本
#!/bin/bash
# 调用expect脚本并传递参数
./auto_ssh.exp $user $password $host
方法2:在Shell中嵌入Expect代码
#!/bin/bash
user="lichu"
pass="lichu123"
host="192.168.1.1"
/usr/bin/expect <<EOF
expect代码
expect代码
.........
expect eof
EOF
2. 结合实战案例
案例1:自动化批量SSH登录执行命令
[root@lichu ~]# vim remote_task.sh
#!/bin/bash
# remote_task.sh
hosts=("10.9.48.121" "10.9.48.200" "10.9.48.246" "server3")
user="lichu"
password="123"
for host in ${hosts[@]};
do
if ! ping -c1 $host &>/dev/null;then
echo "$host 不能连接,请检查"
continue
fi
/usr/bin/expect <<EOF
set timeout 20
spawn ssh $user@$host
expect {
"yes/no" {send "yes\r"; exp_continue}
"password:" {send "$password\r"}
timeout {exit 1}
}
expect "$ " {send "df -h\r"}
expect "$ " {send "free -m\r"}
expect "$ " {send "exit\r"}
expect eof
EOF
echo "===== $host 检查完成 ====="
done
案例2:自动化批量rsync文件复制
[root@lichu ~]# cat hosts.txt
10.9.48.121 root 123
10.9.48.246 lichu 123
[root@lichu ~]# vim remote_copy.sh
#!/usr/bin/bash
# remote_copy.sh
while read line
do
{
read host user password <<<$line
/usr/bin/expect <<-EOF
set timeout 5
spawn rsync -va /etc $user@$host:/tmp
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "$password\r" }
}
expect eof
EOF
}&
done < hosts.txt
wait
echo "all finish..."
案例3:自动化批量公钥推送
[root@lichu ~]# cat hosts.txt
10.9.48.121 root 123
10.9.48.246 lichu 123
[root@lichu ~]# cat push_publickey.sh
#!/usr/bin/bash
# push_publickey.sh
rpm -q expect &>/dev/null
if [ $? -ne 0 ];then
yum -y install expect
fi
if [ ! -f ~/.ssh/id_rsa ];then
ssh-keygen -P "" -f ~/.ssh/id_rsa
fi
while read line
do
{
read host user password <<<$line
ping -c1 $host &>/dev/null
if [ $? -eq 0 ];then
/usr/bin/expect <<-EOF
set timeout 10
spawn ssh-copy-id $user@$host
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "$password\r" }
}
expect eof
EOF
fi
}&
done < hosts.txt
wait
echo "finish......"
[root@lichu ~]# ssh root@10.9.48.121 'hostname'
nfs-server
[root@lichu ~]# ssh lichu@10.9.48.246 'hostname'
lichu
案例4:自动化FTP文件传输【扩展】
#!/bin/bash
# 文件名:auto_ftp.sh
ftp_host="ftp.example.com"
ftp_user="user"
ftp_pass="password"
local_file="/data/upload.txt"
remote_dir="/incoming"
/usr/bin/expect <<EOF
set timeout 60
spawn ftp $ftp_host
expect "Name*" {send "$ftp_user\r"}
expect "Password:" {send "$ftp_pass\r"}
expect "ftp>" {send "cd $remote_dir\r"}
expect "ftp>" {send "put $local_file\r"}
expect "ftp>" {send "bye\r"}
expect eof
EOF
if [ $? -eq 0 ]; then
echo "文件上传成功"
else
echo "文件上传失败"
fi