直接用 uniq 统计日志 IP 次数不可行,因其仅处理相邻重复行;必须先 sort 归组、再 uniq -c 计数,最后 sort -nr 降序输出,完整流程为提取→排序→计数→排序。

直接用 uniq 统计日志里的 IP 次数是行不通的——它只处理相邻重复行,而原始日志中同一 IP 往往分散在各处。必须先用 sort 把相同 IP 归到一起,uniq -c 才能准确计数。
基础流程:提取 → 排序 → 计数 → 排序输出
这是最常用、最稳妥的四步链式操作:
-
提取 IP:用
awk '{print $1}'或cut -d' ' -f1获取每行第一个字段(默认 Apache/Nginx 日志中即客户端 IP) -
排序归组:用
sort把所有相同 IP 排成连续块,为uniq做准备 -
计数去重:
uniq -c自动统计每个连续块出现次数,并在行首显示数字 -
按频次降序:再用
sort -nr按计数列(首字段)数值逆序排列,高频 IP 在前
完整命令示例:
awk '{print $1}' access.log | sort | uniq -c | sort -nr
处理代理场景:正确提取真实客户端 IP
若服务前端有 Nginx 或负载均衡,日志中 $1 可能是内网地址,真实 IP 在 X-Forwarded-For 字段(如 "203.0.113.5, 192.168.1.10")。不能取最后一个,要取最左边那个。
推荐写法(兼容引号与空格):
awk '{if ( ~ /^"/) {gsub(/^"|"$/, "", ); n=split(, a, /,\s*/); print a[1]} else {print }}' access.log | sort | uniq -c | sort -nr
Linux 性能分析与调优专家,覆盖 CPU、内存、磁盘 I/O、网络、内核参数、编译优化、容器/K8s。适用场景:系统卡顿/高负载、内存不足/OOM/Swap 高、CPU 异常/iowait 高。
说明:$2 对应日志中被引号包裹的 X-Forwarded-For 字段;split 按逗号+空格切分后取 a[1],就是真实用户 IP。
过滤干扰数据,提升统计准确性
日志里常混着健康检查、错误请求、空行或注释,会拉低结果可信度。建议在管道早期就过滤:
- 跳过非 IPv4 开头的行:
/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ - 排除常见干扰路径:
!/healthz|favicon\.ico|\/ping/ - 跳过 4xx/5xx 错误响应(可选,需确认状态码在
$9):$9 !~ /^2../
组合后命令更干净:
awk '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ && !/healthz|404/ {print $1}' access.log | sort | uniq -c | sort -nr
大日志文件性能优化:避免三次磁盘读取
当 access.log 超过 1GB,上面的管道会读文件三次(awk、sort、sort),效率低且内存压力大。改用单次扫描的 awk 计数更高效:
awk '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ && !/404/ {ip[$1]++} END {for (i in ip) print ip[i], i}' access.log | sort -nr
注意:ip[$1]++ 在内存中完成计数,END 块统一输出;但唯一 IP 数量极大时(如千万级),内存占用会上升,需权衡。

















