当前位置: 首页 > news >正文

SSH批量连接测试实战

运维生涯中最打脸的经历:本以为一行ssh命令就能搞定的事,折腾了整整一下午。200多个IP,脚本跑了4个就卡死;手动测试明明能连上,自动化脚本却疯狂报错;好不容易跑通了,速度又慢得让人崩溃……

这篇文章,记录了我从“这有什么难的”到“原来水这么深”的全过程。如果你也需要批量测试服务器连接性,这篇实战避坑手册能帮你省下至少一个下午的调试时间。

一、需求:一个看似简单的任务

事情是这样的:某天下午,领导扔过来一个IP列表,大概200多行,要求确认这些服务器的SSH连接是否正常。账号密码统一,只需要测试连通性,2-3秒超时,结果按成功/失败分类保存。

听起来很简单对吗?我当时也是这么想的。

原始需求拆解:

测试一批IP的22端口SSH连接

固定账号:root

固定密码:mn150@2099A

超时控制:2-3秒

结果分类输出:成功IP / 失败IP

支持批量:数百个IP

环境约束:

操作系统:CentOS 7

网络:存在不确定延迟和防火墙策略

性质:测试环境,可使用密码认证

二、技术选型

动手之前,先评估一下有哪些路可以走。

方案实现方式优点缺点适用场景
sshpass + 循环sshpass -p '密码' ssh root@IP简单直接,无额外依赖串行执行,数百IP耗时数分钟少量IP测试
expect脚本spawn ssh+expect "password:"交互处理稳定语法晦涩,调试困难需要复杂交互的场景
Python + Paramikoparamiko.SSHClient().connect()功能全面,错误处理完善需要Python环境,依赖安装需要后续二次开发的场景
GNU parallel并行parallel -j 20速度极快并发控制复杂,文件写入冲突大量IP,追求效率

我最终选择了sshpass + 循环作为基础方案,原因很简单:环境最干净,不需要装任何额外的东西。等基础跑通了,再考虑并行优化。

三、踩坑实录

坑1:脚本只跑了4个IP就停了

现象:执行./ssh_test.sh,只处理了4个IP就莫名其妙退出,没有任何错误提示。

排查:加set -x调试,发现报错:

./ssh_test.sh: line 1: i#!/bin/bash: No such file or directory

原因:脚本文件是从Windows记事本复制过来的,保存成了带BOM头的UTF-8格式,#!/bin/bash前面多了看不见的垃圾字符。

解决方案:

# 查看文件编码 file ssh_test.sh # 方法一:用 sed 清除第一行的垃圾字符 sed -i '1s/^.*#!/#!/' ssh_test.sh # 方法二:用 dos2unix 转换(同时修复换行符问题) yum install -y dos2unix dos2unix ssh_test.sh

教训:从Windows环境复制脚本到Linux,永远先执行dos2unix

坑2:IP文件有200行,脚本只读了4行

现象:确认脚本本身没问题,但while read循环只执行了4次就结束了。

原因:IP文件也是从Windows复制过来的,每行末尾带着\r(CRLF换行符)。read命令读到\r后,把它当作行内容的一部分,导致后续逻辑异常。

解决方案:

# 检查文件中的隐藏字符 cat -A iplist.txt | head -5 # 如果看到 ^M$,说明是Windows换行符 # 修复换行符 dos2unix iplist.txt # 或者用 sed sed -i 's/\r$//' iplist.txt

教训:任何从外部来的文本文件,先做格式清洗。

坑3:手动SSH能通,脚本却一直失败

现象:手动执行ssh root@IP能正常连接,但脚本里的sshpass命令始终返回非0。

排查:对比手动和脚本的SSH选项差异。最终发现是-o BatchMode=yes这个选项在作祟——它会禁用密码认证,导致sshpass根本无法工作。

解决方案:

# 错误配置 sshpass -p '密码' ssh -o BatchMode=yes root@IP # 正确配置(明确允许密码认证) sshpass -p '密码' ssh \ -o BatchMode=no \ -o PasswordAuthentication=yes \ -o StrictHostKeyChecking=no \ root@IP "exit"

教训:SSH选项不是越多越好,BatchMode=yes在自动化密码认证场景下是致命的。

坑4:并发写入导致结果文件错乱

现象:改成并行执行后,结果文件里的IP数量对不上,有的IP重复,有的IP丢失。

原因:多个进程同时向同一个文件追加内容,没有加锁,导致写入冲突。

解决方案:每个进程写入独立的临时文件,最后合并。

TEMP_DIR=$(mktemp -d) # 每个进程写入 $TEMP_DIR/success_$$ # 最后 cat $TEMP_DIR/success_* > success.txt

四、最终稳定版脚本

经过四轮迭代,最终版本具备以下特性:

环境预检(sshpass是否安装、IP文件是否存在)

IP文件智能清洗(去注释、去空行、去Windows换行符、格式校验)

进度显示(百分比 + 动态更新)

结果分类输出(带时间戳,防止覆盖)

统计报告(总数、成功数、失败数、成功率)

临时文件自动清理

#!/bin/bash # SSH批量连接测试脚本 - 最终稳定版 set -u # 使用未定义变量时报错 set -e # 遇到错误时退出(关键函数单独处理) # ==================== 配置参数 ==================== PASSWORD='mn150@2099A' CONNECT_TIMEOUT=3 IP_FILE="${1:-iplist.txt}" # 支持命令行参数 TIMESTAMP=$(date +%Y%m%d_%H%M%S) SUCCESS_FILE="success_ips_${TIMESTAMP}.txt" FAILED_FILE="failed_ips_${TIMESTAMP}.txt" # ==================== 环境验证 ==================== validate_environment() { if ! command -v sshpass &> /dev/null; then echo " sshpass 未安装" echo " 请执行: sudo yum install -y epel-release sshpass" exit 1 fi if [ ! -f "$IP_FILE" ]; then echo "IP文件 $IP_FILE 不存在" exit 1 fi } # ==================== IP列表清洗 ==================== prepare_ip_list() { local input_file="$1" local output_file="$2" > "$output_file" while IFS= read -r line || [ -n "$line" ]; do # 1. 剥离Windows换行符 line=$(echo "$line" | tr -d '\r') # 2. 去除首尾空白 line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') # 跳过空行和注释 [[ -z "$line" || "$line" =~ ^# ]] && continue # 提取IP(移除可能的尾随注释) ip=$(echo "$line" | awk '{print $1}') # 校验IPv4格式 if [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "$ip" >> "$output_file" else echo "跳过无效IP: $ip" >&2 fi done < "$input_file" } # ==================== 单IP测试 ==================== test_single_ip() { local ip="$1" timeout ${CONNECT_TIMEOUT} sshpass -p "$PASSWORD" ssh \ -o ConnectTimeout=${CONNECT_TIMEOUT} \ -o StrictHostKeyChecking=no \ -o PasswordAuthentication=yes \ -o BatchMode=no \ -o LogLevel=ERROR \ root@"$ip" "exit 0" 2>/dev/null return $? } # ==================== 主流程 ==================== main() { echo "========================================" echo " SSH批量连接测试工具" echo "========================================" echo "账号: root" echo "超时: ${CONNECT_TIMEOUT}秒" echo "IP文件: $IP_FILE" echo "成功输出: $SUCCESS_FILE" echo "失败输出: $FAILED_FILE" echo "========================================" validate_environment # 清洗IP列表 local clean_ip_file="/tmp/clean_ips_$$.txt" prepare_ip_list "$IP_FILE" "$clean_ip_file" local total_ips=$(wc -l < "$clean_ip_file" 2>/dev/null || echo 0) if [ "$total_ips" -eq 0 ]; then echo "没有有效的IP地址" exit 1 fi echo "找到 $total_ips 个有效IP" echo "" # 清空结果文件 > "$SUCCESS_FILE" > "$FAILED_FILE" echo "开始测试..." echo "----------------------------------------" local tested=0 success=0 failed=0 while read ip; do tested=$((tested + 1)) # 进度显示(每10个或最后一个更新一次) if [ $((tested % 10)) -eq 0 ] || [ "$tested" -eq "$total_ips" ]; then printf "\r📊 进度: %d/%d (%.1f%%)" "$tested" "$total_ips" \ "$(echo "scale=1; $tested*100/$total_ips" | bc)" fi if test_single_ip "$ip"; then echo "$ip" >> "$SUCCESS_FILE" success=$((success + 1)) else echo "$ip" >> "$FAILED_FILE" failed=$((failed + 1)) fi done < "$clean_ip_file" echo "" echo "----------------------------------------" echo "" echo "========== 测试报告 ==========" echo "总IP数: $total_ips" echo "成功: $success" echo "失败: $failed" if [ "$total_ips" -gt 0 ]; then echo "成功率: $(echo "scale=2; $success*100/$total_ips" | bc)%" fi echo "" echo "结果文件:" echo "$SUCCESS_FILE" echo "$FAILED_FILE" # 展示前5个成功的IP if [ -s "$SUCCESS_FILE" ]; then echo "" echo "成功IP示例(前5个):" head -5 "$SUCCESS_FILE" | cat -n fi # 清理临时文件 rm -f "$clean_ip_file" } main

五、核心技术要点总结

5.1 健壮的IP文件处理模板

while IFS= read -r line || [ -n "$line" ]; do line=$(echo "$line" | tr -d '\r') # 干掉Windows换行符 line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') # 去首尾空白 [[ -z "$line" || "$line" =~ ^# ]] && continue # 跳过空行和注释 ip=$(echo "$line" | awk '{print $1}') # 提取第一列 [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] && echo "$ip" done < ipfile.txt

5.2 安全稳定的SSH选项组合

选项作用
ConnectTimeout3连接超时,避免卡死
StrictHostKeyCheckingno测试环境跳过主机密钥确认
PasswordAuthenticationyes明确允许密码认证
BatchModeno不禁用密码认证
LogLevelERROR只输出错误,减少干扰

5.3 退出码判断参考

timeout $TIMEOUT sshpass ... case $? in 0) echo "连接成功" ;; 124) echo "连接超时" ;; 5) echo "认证失败(密码错误或账号被禁用)" ;; *) echo "其他错误(网络不通、端口关闭等)" ;; esac

六、运维经验沉淀

经过这次实战,总结了几条值得记住的原则:

原则说明
小范围验证先用2-3个IP测试脚本逻辑,确认无误再批量跑
格式清洗前置任何外部来的文本文件,先dos2unix再处理
逐层加功能先跑通基础逻辑 → 加进度显示 → 加错误处理 → 加并行
临时文件隔离并行场景下,每个进程写入独立文件,最后合并
留下日志脚本执行时间、结果统计、异常信息都记录下来

批量测试SSH连接,表面上是技术问题,本质上是对细节的把控,编码格式、换行符、SSH选项、并发冲突,任何一个细节没处理好,整个流程就会卡住。

好的自动化脚本不是一蹴而就的,而是在不断遇到问题、解决问题的过程中逐渐打磨出来的。希望这篇踩坑实录能帮你避开我走过的弯路。

http://www.jsqmd.com/news/811759/

相关文章:

  • 长期使用后回顾平台账单的清晰度与用量分析的便利性
  • 079、多轴运动控制:插补器设计(圆弧插补)
  • Day30:Redis 缓存策略 + 菜单实战缓存 + 三大缓存问题(穿透 / 击穿 / 雪崩)
  • 从 3D Gaussian Splatting 到具身智能:AI 正在学会“进入世界”
  • 别再空谈帕累托最优了!用Python+Excel手把手教你做资源分配决策分析
  • 开源智能抓取框架:为低成本机械爪赋予视觉与决策能力
  • Word公式转MathType:从批量转换报错到权限配置的实战复盘
  • 手机号逆向查询QQ号:3分钟掌握终极查询技巧
  • EdgeCIM框架:存内计算技术如何优化边缘设备上的小型语言模型
  • 多模态大模型学习笔记(三十九)——生成式与Transformer式OCR:从“像素抄录“到“文档智能“的完整演进
  • 智能工厂的核心交互:薄膜开关技术在新型基础设施中的关键作用
  • 五款API管理系统的功能体系与数据表现
  • 使用TaotokenTokenPlan套餐在长期项目中获得更大优惠的方法
  • Taotoken多模型聚合平台为arm7边缘AI应用提供稳定API服务
  • “柔”不是降低饱和度!Pastel印相的光学衍射模拟原理,及如何用--chaos 23–47精准控制粉彩颗粒噪点分布
  • Unlock-Music:浏览器音乐解锁工具完全指南
  • Python环境PyTorch无法调用GPU_检查CUDA驱动与版本匹配性
  • 覆盖成都各区的川师大家教网(大学生创业平台),怎么帮孩子挑个合适的学霸老师? - 教育快讯速递
  • 从图像融合到系统设计:EDA工程师的跨界思维迁移与工具选型实践
  • A1 学习速查表
  • 2026年GEO优化服务商口碑哪家好?案例验证与服务响应深度解析 - 科技焦点
  • 兔抗FANCI抗体亲和纯化,IP-WB全流程兼容设计,一站式解决FANCI蛋白分析功能
  • 从接入到上线观察 Taotoken 对开发者体验的整体提升
  • Arm Cortex-R52处理器流水线优化与指令调度实战
  • 2026年三款最值得在线预约小程序,解决您的预约难题
  • 在Windows上安装安卓应用的轻量化实践
  • 用ChatGPT 10分钟生成TikTok爆款脚本:5步工作流+3类高转化话术模板(附Prompt库下载)
  • 【OAI实战】基于Docker-Compose的5G核心网基础部署与排错指南
  • 实习期如何脱颖而出?3个月转正答辩的满分操作
  • 2026年GEO优化公司专业性评测:五大服务商技术能力深度对比 - 科技焦点