Shell脚本如何批量执行远程命令:自动化运维的终极指南
目录导读
-
为什么需要批量执行远程命令?
现代运维中,管理成百上千台服务器已成为常态,手动SSH连接每台机器执行命令不仅低效,还容易出错,Shell脚本结合SSH、expect、pssh等工具,能实现一键批量操作。
-
核心技术原理
- SSH免密登录配置
- 循环与并行执行策略
- 错误处理与日志记录
-
四种主流实现方案
for循环 +ssh(最基础)pssh(并行SSH)expect+ssh(处理交互式密码)ansible临时命令(大规模首选)
-
安全与优化技巧
- 密钥管理与权限控制
- 超时与重试机制
- 结果统一收集
-
常见问题问答(FAQ)
- Q: 如何跳过首次连接的主机密钥确认?
- Q: 脚本中密码明文如何避免?
- Q: 某台机器执行失败怎么单独重试?
为什么需要批量执行远程命令?
在运维场景中,你可能会遇到以下需求:
- 对100台Web服务器统一更新配置文件
- 批量检查所有数据库服务器的磁盘使用率
- 同时重启多个微服务节点
手动操作不仅耗时,而且容易因为漏执行某台机器导致故障,Shell脚本通过自动化批量执行,能实现:
- 一致性:每台机器执行完全相同的命令
- 效率:从小时级缩短到分钟级
- 可复现:脚本可以版本化保存,避免人工误操作
根据Google SEO最佳实践,本文聚焦于实用、可执行、安全性三个维度,帮助你快速搭建自己的批量执行工具。
核心技术原理
基础前提:SSH免密登录
批量执行的核心依赖SSH,而密码输入会中断自动化流程,首先配置免密登录:
# 在控制节点生成密钥对 ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" # 将公钥复制到目标机器 ssh-copy-id user@server1 ssh-copy-id user@server2
SEO提示:若你管理AWS/阿里云等云服务器,建议使用安全组限制控制节点的IP白名单,这是第一层防护。
执行策略:串行 vs 并行
- 串行:
for循环逐个执行,适合命令依赖上一步结果 - 并行:使用
&后台运行或pssh,适合独立命令(如检查状态)
示例对比:
# 串行(慢但可控)
for host in $(cat hosts.txt); do
ssh user@$host "uptime"
done
# 并行(快但需留意资源)
for host in $(cat hosts.txt); do
ssh user@$host "uptime" &
done
wait
四种主流实现方案
for循环 + SSH(入门级)
适用场景:机器数量<30,命令简单,无需批量结果收集。
#!/bin/bash
# 文件名:batch_ssh.sh
HOSTS="192.168.1.10 192.168.1.11 192.168.1.12"
COMMAND="df -h / | tail -1"
for ip in $HOSTS; do
echo "===== 正在连接 $ip ====="
ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no user@$ip "$COMMAND"
if [ $? -ne 0 ]; then
echo "错误:$ip 执行失败" >> error.log
fi
done
关键参数说明:
-o StrictHostKeyChecking=no:跳过首次连接确认(仅安全内网使用)ConnectTimeout=5:防止卡死在某台僵尸机器
pssh(并行SSH)
适用场景:中等规模(50-200台),需要并行加速。
安装:yum install pssh 或 pip install pssh
# hosts.txt 每行一个:user@ip # -i:显示输出 -h:指定主机列表 -t:超时秒数 pssh -i -h hosts.txt -t 10 "uptime"
优势:自动并行,输出会按主机前缀分组,方便查看。
expect + SSH(处理密码场景)
适用场景:无法配置免密登录(如第三方托管服务器)。
#!/usr/bin/expect
# 文件名:ssh_with_pass.exp
set timeout 5
set password "your_password"
set command "hostname"
spawn ssh user@192.168.1.10 $command
expect {
"password:" { send "$password\r" }
"yes/no" { send "yes\r"; exp_continue }
}
expect eof
注意:密码明文存储有风险,可通过环境变量或加密文件传递。
Ansible 临时命令(企业级)
适用场景:大规模集群,需要结果收集、失败重试、权限管理。
# 安装 ansible(控制节点) pip install ansible # 配置 /etc/ansible/hosts 或 inventory 文件 [webservers] 192.168.1.10 192.168.1.11 # 执行临时命令 ansible webservers -m shell -a "df -h /" -u user --private-key=~/.ssh/id_rsa # 收集失败信息(看看谁失败了) ansible webservers -m shell -a "df -h /" --fail-fast
优势:内置幂等性、并行控制、结果JSON化,适合与监控系统集成。
安全与优化技巧
密钥管理最佳实践
- 专用密钥:使用
ssh-keygen -t ed25519替代RSA(更安全) - 跳板机:通过跳板机管控所有目标机器,不直接暴露SSH端口
- 定期轮换:使用
ssh-keygen -R server_ip移除已知主机(应对IP变化)
超时与重试机制
# 带重试的循环(最多重试3次)
for host in $(cat hosts.txt); do
retry=3
while [ $retry -gt 0 ]; do
ssh -o ConnectTimeout=3 $host "command" && break
retry=$((retry - 1))
sleep 2
done
done
结果统一收集
方案:将输出重定向到以主机名命名的文件
mkdir -p results
for host in $(cat hosts.txt); do
ssh $host "command" > "results/${host}.log" 2>&1 &
done
wait
grep -r "ERROR" results/ # 快速定位问题
常见问题问答(FAQ)
Q1: 如何跳过首次连接的主机密钥确认?
A: 在/etc/ssh/ssh_config中全局设置StrictHostKeyChecking no,或命令行加-o StrictHostKeyChecking=no,但注意:这降低安全等级,仅建议测试环境使用,生产环境应使用known_hosts预分发。
Q2: 脚本中密码明文如何避免?
A: 三种方案:
- 使用
ssh-agent+ 密钥(推荐) - 将密码存入加密文件(如
gpg -e password.txt) - 采用
ansible-vault加密变量
Q3: 某台机器执行失败怎么单独重试?
A: 将失败IP存入独立文件,然后针对性重试:
# 从日志提取失败主机
grep -l "ERROR" results/*.log | sed 's/results\/\(.*\)\.log/\1/' > fail_hosts.txt
# 重试
for host in $(cat fail_hosts.txt); do
ssh $host "command"
done
Q4: 批量执行命令时,如何区别不同机器的输出?
A: 使用pssh -i(自带前缀)或自定义前缀:
ssh $host "echo "[$host] 状态:"; uptime"
Q5: 脚本能否同时执行多个不同的命令?
A: 可以!将命令放入变量或函数:
CMDS=("hostname" "uptime" "free -m")
for cmd in "${CMDS[@]}"; do
pssh -h hosts.txt "$cmd"
done
批量执行远程命令是运维人员的必修课,从简单的for循环到ansible企业级方案,选择取决于你的机器数量、安全要求、团队技能,建议先从方案一开始,逐步过渡到并行pssh,最后在需要跨团队协作时引入ansible或saltstack。
最后提醒:永远先在测试环境验证脚本;永远记录日志;永远最小权限原则,自动化不是魔法,而是严谨的工程实践。
本文综合多个搜索引擎的实战案例,去伪存真后提炼为可落地执行的精华内容,如需转载,请保留完整性。