文章目录
核心目标
本脚本旨在提供一个轻量级、跨平台、实时可视化的网络质量监控解决方案,核心解决以下问题:
- 多维度质量评估:传统工具仅测连通性,本脚本结合 Ping(延迟、丢包率)与 Curl(实际下载速率),全面反映用户体验。
- 跨平台兼容性 :自动识别 macOS (Darwin) 与主流 Linux 发行版(CentOS/Ubuntu),屏蔽
ping超时参数单位等底层差异,实现"一次编写,到处运行"。 - 实时网络波动:采用终端原地刷新机制,以固定频率(默认 5 秒)更新看板,直观展示实时波动。
- 直观状态判断:基于预设阈值将延迟/网速自动划分为"优秀、良好、一般、较差、极差"五个等级,通过颜色编码(绿/黄/红)快速定位瓶颈。
- 高效故障定位:自动统计失败站点列表,帮助运维/开发者迅速判断是局部服务不可用还是整体网络中断。
脚本功能详解
该脚本 (network-monitor.sh) 集成以下关键功能模块:
混合探测机制 (Ping + Curl)
- Ping 探测 :
- 使用
ping -c 1单次快速探测,减少等待时间。 - 智能解析
min/avg/max往返时间统计行,提取平均延迟。 - 计算丢包率,若丢包率 100% 则直接判定超时,跳过后续测速以节省资源。
- 使用
- Curl 测速 :
- 仅对 Ping 成功的站点执行,避免在不可达主机上浪费超时时间。
- 使用
curl -L自动跟随 HTTP 301/302 跳转,确保测试最终落地页(如www.baidu.com跳转至 HTTPS 首页)。 - 利用
-%{speed_download}获取实时下载速度(字节/秒),转换为人类可读的KB/s。 - 设置严格总超时(默认 3 秒),防止慢速响应阻塞主循环。
自适应阈值评级系统
内置两套独立评级标准,分别针对延迟 和带宽:
- 延迟评级 :
- 🟢 优秀:< 50ms (适合实时游戏、高频交易)
- 🟢 良好:50-100ms (适合视频会议、流畅浏览)
- 🟡 一般:100-200ms (普通网页浏览可接受)
- 🔴 较差:200-500ms (明显卡顿)
- 🔴 极差:> 500ms (几乎不可用)
- 网速评级 (基于 KB/s):
- 🟢 优秀:> 250 KB/s (>2Mbps,加载大图/视频流畅)
- 🟢 良好:150-250 KB/s
- 🟡 一般:60-150 KB/s (纯文本/小图正常)
- 🔴 较差:20-60 KB/s
- 🔴 极差:< 20 KB/s
精确的时间控制循环
- 解决传统
sleep导致的累积延迟问题。 - 逻辑:
睡眠时间 = 设定间隔 - (当前时间 - 循环开始时间)。 - 即使某个站点探测耗时较长,也能保证下一次刷新贴近设定时间点(如每 5 秒整),保持监控节奏稳定。
- 首次运行立即执行,无初始等待。
依赖自检与容错
- 启动时自动检查
bc(计算器) 是否安装,未安装时给出针对 macOS (brew) 和 Linux (apt/yum) 的具体安装指令。 - 正则表达式校验
curl返回的速度值,防止非数字字符导致脚本崩溃。 - 处理
ping输出格式在 macOS 和 Linux 下的细微差异。
脚本代码
bash
#!/bin/bash
# ==============================
# 实时网络质量监控脚本 (network-monitor.sh)
# 兼容:macOS (Darwin) + Linux (CentOS/Ubuntu)
# 功能:
# - Ping 测试:评估连通性与延迟
# - Curl 测试:对可达站点测速(KB/s),自动跟随 HTTP 跳转(-L)
# 用法:chmod +x network-monitor.sh && ./network-monitor.sh
# 退出:按 Ctrl+C
# ==============================
# ┌──────────────────────────────┐
# │ 可配置区域(请在此修改) │
# └──────────────────────────────┘
# 刷新间隔(秒)
REFRESH_INTERVAL=5
# 监控站点列表(格式:"地址|说明")
SITES=(
"www.baidu.com|百度首页"
"www.tencent.com|腾讯首页"
"www.taobao.com|淘宝首页"
"www.douyin.com|抖音首页"
"doubao.com|豆包AI"
"qianwen.com|千问AI"
"yuanbao.tencent.com|元宝AI"
# "192.0.2.1|失败测试"
)
# ===== Ping 延迟阈值(单位:毫秒)=====
EXCELLENT_THRESHOLD=50 # 优秀
GOOD_THRESHOLD=100 # 良好
FAIR_THRESHOLD=200 # 一般
POOR_THRESHOLD=500 # 较差
# ===== Curl 网速阈值(单位:KB/s)=====
SPEED_EXCELLENT=250 # 优秀(>250 KB/s,对应淘宝257.9、腾讯382.6等峰值站点)
SPEED_GOOD=150 # 良好(150~250 KB/s,对应抖音157.7、豆包199.4、腾讯176.9等)
SPEED_FAIR=60 # 一般(60~150 KB/s,对应百度83.8、淘宝128.7、千问75.7等主流站点)
SPEED_POOR=20 # 较差(20~60 KB/s,对应百度41.6、元宝43.7等低值站点)
# <20 KB/s 视为极差(对应抖音10.5 KB/s等最低值)
# Ping 参数
PING_COUNT=1 # 每个站点 ping 次数
PING_TIMEOUT_SEC=1 # 单个 ping 包超时(秒)
# Curl 网速测试超时(秒),仅用于 curl -m
CURL_TIMEOUT_SEC=3 # 不在界面展示,仅内部使用
# ┌──────────────────────────────┐
# │ 通用配置 │
# └──────────────────────────────┘
# 跨平台 Ping 超时参数
if [[ "$OSTYPE" == "darwin"* ]]; then
PING_TIMEOUT_MS=$(echo "scale=0; $PING_TIMEOUT_SEC * 1000" | bc -l)
PING_W_PARAM="-W $PING_TIMEOUT_MS"
else
PING_W_PARAM="-W $PING_TIMEOUT_SEC"
fi
# 颜色定义
RED=$'\033[0;31m'
GREEN=$'\033[0;32m'
YELLOW=$'\033[1;33m'
BLUE=$'\033[0;34m'
CYAN=$'\033[0;36m'
NC=$'\033[0m' # No Color
# 检查依赖
check_dependencies() {
if ! command -v bc &> /dev/null; then
if [[ "$OSTYPE" == "darwin"* ]]; then
echo "❌ 未安装 bc,请执行:brew install bc"
else
echo "❌ 未安装 bc,请执行:sudo apt install bc 或 yum install bc"
fi
exit 1
fi
}
# ┌──────────────────────────────┐
# │ 核心函数:测试单个站点(含 Ping + Curl) │
# └──────────────────────────────┘
test_site() {
local entry="$1"
IFS='|' read -r site desc <<< "$entry"
local output ping_exit_code packet_loss loss_percent success_count
local stats_line numbers avg avg_formatted result=""
local is_success=0
# 执行 Ping
LC_ALL=C
output=$(LC_ALL=C ping -c "$PING_COUNT" $PING_W_PARAM "$site" 2>&1)
ping_exit_code=$?
# 解析丢包率
packet_loss=$(echo "$output" | grep -Eo '[0-9.]+% packet loss' | awk '{print $1}' | tr -d '%')
if [ -n "$packet_loss" ]; then
loss_percent=$(echo "$packet_loss" | bc -l)
success_count=$(echo "scale=0; $PING_COUNT * (100 - $loss_percent) / 100" | bc -l)
else
if [ "$ping_exit_code" -eq 0 ]; then
success_count=$PING_COUNT
else
success_count=0
fi
fi
# 初始化网速相关变量
local speed_kbps=0
local speed_status=""
if [ "$success_count" -gt 0 ]; then
# 提取延迟统计
if [[ "$OSTYPE" == "darwin"* ]]; then
stats_line=$(echo "$output" | grep "round-trip" | head -n1)
else
stats_line=$(echo "$output" | grep -E "(min/avg/max|rtt)" | head -n1)
fi
if [ -n "$stats_line" ]; then
numbers=$(echo "$stats_line" | grep -o '[0-9.]\+/[0-9.]\+/[0-9.]\+' | head -n1)
if [ -n "$numbers" ]; then
IFS='/' read -r min avg max <<< "$numbers"
avg_formatted=$(printf "%.1f" "$avg")
is_success=1
fi
fi
# === 对 Ping 成功的站点进行网速测试 ===
# 使用 http:// 触发跳转,-L 自动跟随 HTTP 301/302
local curl_url="http://$site"
local curl_result
# -L: follow redirects; -m: total timeout
curl_result=$(curl -L -m "$CURL_TIMEOUT_SEC" -s -o /dev/null -w "%{speed_download}" "$curl_url" 2>/dev/null)
# 检查 curl 是否返回有效数字
if [[ "$curl_result" =~ ^[0-9]+(\.[0-9]+)?$ ]] && (( $(echo "$curl_result > 0" | bc -l) )); then
speed_kbps=$(awk "BEGIN {printf \"%.1f\", $curl_result / 1024}")
# 判定网速状态
if (( $(echo "$speed_kbps >= $SPEED_EXCELLENT" | bc -l) )); then
speed_status="${GREEN}� 优秀${NC}"
elif (( $(echo "$speed_kbps >= $SPEED_GOOD" | bc -l) )); then
speed_status="${GREEN}� 良好${NC}"
elif (( $(echo "$speed_kbps >= $SPEED_FAIR" | bc -l) )); then
speed_status="${YELLOW}� 一般${NC}"
elif (( $(echo "$speed_kbps >= $SPEED_POOR" | bc -l) )); then
speed_status="${RED}� 较差${NC}"
else
speed_status="${RED}� 极差${NC}"
fi
else
# curl 失败或速率为 0(如超时、空响应)
speed_kbps="N/A"
speed_status="${YELLOW}⚠️ 无数据${NC}"
fi
# 构建结果字符串(含延迟 + 网速)
if [ "$is_success" -eq 1 ]; then
result="${site}(${desc}): ${avg_formatted} ms | ${speed_kbps} KB/s ($speed_status)"
else
result="${site}(${desc}): 成功但无延迟数据 | ${speed_kbps} KB/s ($speed_status)"
fi
echo "1|$avg|$speed_kbps|$result"
return
else
# Ping 完全失败,不进行网速测试
result="${site}(${desc}): ${RED}超时${NC} | --- KB/s (---)"
echo "0|0|0|$result"
return
fi
}
# ┌──────────────────────────────┐
# │ 主程序逻辑 │
# └──────────────────────────────┘
check_dependencies
clear
echo -e "${CYAN}� 实时网络质量监控(${OSTYPE})| 每 ${REFRESH_INTERVAL} 秒刷新 | Ctrl+C 退出 ${NC}"
echo -e "${BLUE}Ping: ${PING_COUNT} 次/站点 | Curl 网速测试超时: ${CURL_TIMEOUT_SEC}s ${NC}\n"
# 主循环:修复首次刷新延迟问题
first_run=true
while true; do
start_time=$(date +%s.%N)
total_avg=0
total_speed=0
valid_count=0
speed_valid_count=0
failed_sites=()
site_results=()
for entry in "${SITES[@]}"; do
test_result=$(test_site "$entry")
IFS='|' read -r is_success avg speed_kbps result <<< "$test_result"
if [ "$is_success" -eq 1 ]; then
total_avg=$(echo "$total_avg + $avg" | bc -l)
((valid_count++))
# 累加有效网速(用于计算平均网速)
if [[ "$speed_kbps" =~ ^[0-9.]+$ ]]; then
total_speed=$(echo "$total_speed + $speed_kbps" | bc -l)
((speed_valid_count++))
fi
else
IFS='|' read -r site desc <<< "$entry"
failed_sites+=("$site")
fi
site_results+=("$result")
done
# 计算整体平均延迟
if [ "$valid_count" -gt 0 ]; then
overall_avg=$(echo "scale=2; $total_avg / $valid_count" | bc -l)
avg_display=$(printf "%.1f" "$overall_avg")
else
overall_avg=9999
avg_display="N/A"
fi
# 判定网络延迟状态(原"网络状态")
if [ "$valid_count" -eq 0 ]; then
delay_status="${RED}❌ 网络中断${NC}"
elif (( $(echo "$overall_avg <= $EXCELLENT_THRESHOLD" | bc -l) )); then
delay_status="${GREEN}� 优秀${NC}"
elif (( $(echo "$overall_avg <= $GOOD_THRESHOLD" | bc -l) )); then
delay_status="${GREEN}� 良好${NC}"
elif (( $(echo "$overall_avg <= $FAIR_THRESHOLD" | bc -l) )); then
delay_status="${YELLOW}� 一般${NC}"
elif (( $(echo "$overall_avg <= $POOR_THRESHOLD" | bc -l) )); then
delay_status="${RED}� 较差${NC}"
else
delay_status="${RED}� 极差${NC}"
fi
# 计算平均网速(仅基于有效测速)
if [ "$speed_valid_count" -gt 0 ]; then
avg_speed=$(echo "scale=2; $total_speed / $speed_valid_count" | bc -l)
speed_display=$(printf "%.1f" "$avg_speed")
else
speed_display="N/A"
fi
# 判定整体网速状态(基于平均网速)
if [ "$speed_valid_count" -eq 0 ]; then
speed_status="${YELLOW}⚠️ 无有效测速${NC}"
elif (( $(echo "$avg_speed >= $SPEED_EXCELLENT" | bc -l) )); then
speed_status="${GREEN}� 优秀${NC}"
elif (( $(echo "$avg_speed >= $SPEED_GOOD" | bc -l) )); then
speed_status="${GREEN}� 良好${NC}"
elif (( $(echo "$avg_speed >= $SPEED_FAIR" | bc -l) )); then
speed_status="${YELLOW}� 一般${NC}"
elif (( $(echo "$avg_speed >= $SPEED_POOR" | bc -l) )); then
speed_status="${RED}� 较差${NC}"
else
speed_status="${RED}� 极差${NC}"
fi
# === 原地刷新输出 ===
clear
echo -e "${CYAN}� 实时网络质量监控(${OSTYPE})| 每 ${REFRESH_INTERVAL} 秒刷新 | Ctrl+C 退出 ${NC}"
echo -e "${BLUE}Ping: ${PING_COUNT} 次/站点 | Curl 网速测试超时: ${CURL_TIMEOUT_SEC}s ${NC}\n"
# 输出核心统计
echo "� 整体平均延迟: ${CYAN}${avg_display} ms${NC} � 网络延迟状态: $delay_status"
echo "� 平均下载速率: ${CYAN}${speed_display} KB/s${NC} � 网速状态: $speed_status"
echo "✅ 有效站点: $valid_count/${#SITES[@]} ❌ 失败站点: ${#failed_sites[@]}"
echo
echo "${BLUE}各站点详情(延迟 | 网速): ${NC}"
for res in "${site_results[@]}"; do
echo " • $res"
done
if [ ${#failed_sites[@]} -gt 0 ]; then
echo
echo "${YELLOW}⚠️ 失败站点列表: ${NC} ${failed_sites[*]}"
fi
# === 修复关键:首次运行不 sleep,后续才 sleep ===
if [ "$first_run" = true ]; then
first_run=false
else
elapsed=$(echo "$(date +%s.%N) - $start_time" | bc)
sleep_time=$(echo "$REFRESH_INTERVAL - $elapsed" | bc)
if (( $(echo "$sleep_time > 0" | bc -l) )); then
sleep "$sleep_time"
fi
fi
done
部署与运行指南
环境准备
确保系统已安装 bash, ping, curl, bc:
-
macOS :
bc通常未预装,需执行:bashbrew install bc -
CentOS/RHEL :
bashsudo yum install bc -y -
Ubuntu/Debian :
bashsudo apt install bc -y
脚本配置
编辑脚本中的 可配置区域 以适配监控需求:
- SITES : 修改监控域名列表,格式为
"域名|中文备注",支持注释掉不需要的站点。 - REFRESH_INTERVAL: 调整刷新频率,建议 5-10 秒,过短会因网络耗时导致刷新延迟。
- 阈值调整 : 根据实际网络环境(内网、专线、弱网)调整
EXCELLENT_THRESHOLD等变量。
执行方式
赋予执行权限并运行:
bash
chmod +x network-monitor.sh
./network-monitor.sh
退出监控
在终端按下 Ctrl + C 即可安全停止脚本。
输出界面示例
脚本运行后,终端将呈现如下动态看板(颜色在实际终端中显示):
text
🌐 实时网络质量监控(linux-gnu)| 每 5 秒刷新 | Ctrl+C 退出
Ping: 1 次/站点 | Curl 网速测试超时: 3s
📊 整体平均延迟: 45.2 ms 🟢 网络延迟状态: ✅ 优秀
🚀 平均下载速率: 185.4 KB/s 🟢 网速状态: ✅ 良好
✅ 有效站点: 7/7 ❌ 失败站点: 0
各站点详情(延迟 | 网速):
• www.baidu.com(百度首页): 32.5 ms | 83.8 KB/s (🟡 一般)
• www.tencent.com(腾讯首页): 28.1 ms | 382.6 KB/s (🟢 优秀)
• www.taobao.com(淘宝首页): 41.0 ms | 128.7 KB/s (🟡 一般)
• www.douyin.com(抖音首页): 55.3 ms | 10.5 KB/s (🔴 极差)
• doubao.com(豆包AI): 48.2 ms | 199.4 KB/s (🟢 良好)
• qianwen.com(千问AI): 39.5 ms | 75.7 KB/s (🟡 一般)
• yuanbao.tencent.com(元宝AI): 62.1 ms | 43.7 KB/s (🔴 较差)
注:若出现站点完全不可达,底部会额外显示黄色警告行:
⚠️ 失败站点列表:192.0.2.1
技术实现细节分析
跨平台 Ping 超时处理
Linux 的 ping -W 单位为秒(支持小数),macOS 的 ping -W 单位为毫秒。脚本通过检测 $OSTYPE 动态转换:
bash
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS: 秒转毫秒
PING_TIMEOUT_MS=$(echo "scale=0; $PING_TIMEOUT_SEC * 1000" | bc -l)
PING_W_PARAM="-W $PING_TIMEOUT_MS"
else
# Linux: 直接使用秒
PING_W_PARAM="-W $PING_TIMEOUT_SEC"
fi
数据解析鲁棒性
针对不同操作系统 ping 输出格式差异(Linux 显示 rtt min/avg/max/mdev,macOS 显示 round-trip min/avg/max/stddev),脚本采用通用正则匹配策略:
- Linux:
grep -E "(min/avg/max|rtt)" - macOS:
grep "round-trip"
提取数字/数字/数字格式字符串后,利用IFS='/'分割获取平均值。
浮点数运算
Bash 原生不支持浮点运算,脚本全程依赖 bc 工具进行精度控制:
- 丢包率计算:
scale=0; $PING_COUNT * (100 - $loss_percent) / 100 - 速度单位换算:
awk "BEGIN {printf \"%.1f\", $curl_result / 1024}"(混用awk格式化,bc做逻辑判断)。 - 阈值比较:
(( $(echo "$speed_kbps >= $SPEED_EXCELLENT" | bc -l) ))。
原子化输出与刷新
为避免输出乱码,脚本先将所有结果收集到数组 site_results 和变量中,计算完整体统计值后,执行一次 clear 清屏,再按顺序打印头部、统计摘要、详细列表和警告信息。这种"全量重绘"方式保证了界面整洁和数据同步性。