#!/usr/bin/expect 是专门处理交互的脚本解释器,适合自动响应 SSH、FTP 等需要手动输入的命令
#安装工具
sudo dnf install expect -y
通常用于自动化 SSH 登录、安装脚本、密码输入等需要用户交互的任务。
expect 脚本一般包含以下几个基本元素:
-
spawn:用于启动需要自动化的命令或程序。
-
expect:定义程序期望看到的输出,expect 会等待输出内容符合条件时再进行下一步。
-
send:向程序发送字符串,如输入命令或确认。
-
interact:允许用户和脚本的交互,通常在脚本末尾使用,以保持会话打开。
来看一个ssh自动登录脚本
先把之前的免密登录文件删除
rm -rf ~/.ssh/*
#!/bin/expect
#设置变量
set timeout 10
set name "root"
set password "123456"
#启动命令
spawn ssh root@192.168.88.102
#匹配期望的输出
expect{
"yes/no"{
send "yes\r"
exp_continue
}
"password:"{
send "$password\r"
}
}
#保持会话交互
interact
-
set timeout 10:设置超时时间为 10 秒,如果 10 秒内没匹配到password:提示,脚本会自动退出,避免卡住。 -
set password "123456":把服务器密码存到变量里,后面自动发送。 -
spawn是expect的核心命令,用来启动一个交互进程,这里就是启动ssh登录服务器 -
expect { "password:" { ... } }:等待 SSH 输出包含password:的提示。 -
exp_continue是expect脚本里的内置命令,作用是:当前分支匹配完成后,继续监听下一个 expect 分支,而不是直接退出 expect 块。
-
send "$password\r":匹配到提示后,自动发送变量里的密码,\r模拟按下回车键。 -
interact:这行是关键!它会把登录后的控制权交还给你,让你能正常操作服务器。如果没有这行,脚本登录成功后会直接退出
全自动免交互 SSH 登录,脚本2
上一个是"固定服务器专用版",这个是 "通用参数版"。参数版适合需要频繁切换服务器、批量登录的场景。
#!/bin/expect
set timeout 10
#0代表第一个参数
set name [ lindex $argv 0 ]
set ip [ lindex $argv1 ]
set password [ lindex $argv 2 ]
#开始执行命令
spawn ssh $name@$ip
#开始匹配要交互的行的关键字
expect{
"yes/no"{
send "yes\r"
exp_continue
}
"password:"{
sent "$password\r"
}
}
#保持交互状态
interact
- 命令行参数的索引从 0 开始,
$argv 0是脚本的第 1 个参数,$argv 1是第 2 个,以此类推。
传参的时候的格式是 ./ssh_login.exp 用户名 服务器IP 密码
(ssh_login.exp(你自己保存的名字)
密码需要用双引号包起来
shell脚本和expect结合使用,在多台服务器上创建1个用户
以#!/bin/bash开头,expect 的交互脚本,必须写在 <<-EOF 和 EOF 这两个标记之间。
<<-EOF:表示 "接下来的内容,直到遇到EOF标记为止,都作为expect命令的输入"。- 结尾的
EOF:是结束标记,告诉 Shell 「Here Document」的内容到这里结束。 - 中间的所有代码,都会被直接传给
expect去执行,实现自动化交互。
完整场景解析(多服务器批量创建用户)
这是一个典型的 bash + expect 组合脚本,用来在多台服务器上批量创建用户,逻辑是:
- bash 部分 :
- 读取
data.txt里的服务器信息(IP、用户名、密码)。 - 循环每一行,解析出
ip、name、pwd1变量。
- 读取
- expect 部分(放在
<<-EOF和EOF之间) :- 用
spawn ssh $name@$ip发起 SSH 连接。 - 自动处理交互(比如
yes/no主机确认、密码输入)。 - 在远程服务器上执行
useradd yh1命令创建用户。
- 用
注意:bash中定义的变量,在expect中可以使用,但是expect定义的变量,bash不能用
1,在管理机上写一个服务器列表文件
vim data.txt
给脚本用的「服务器清单」,里面写的是要管理的所有服务器的信息
192.168.10.10 root 123456
192.168.10.11 root 123456
192.168.10.12 root 123456
这个文件不用写在脚本里,是因为:
- 服务器信息可能会变(比如新增 / 下线服务器、改了密码),直接改文件比改脚本方便太多。
- 脚本是通用的,换一批服务器,只要换个
data.txt就行,不用改代码。