核心思路是用 date 生成一小时前时间戳,awk 按日志第4字段前缀匹配并过滤2xx状态及get/post请求,用 match 提取双引号内url路径,再经 sort | uniq -c | sort -nr | head -20 统计top20。

用 awk + sort + head 快速提取访问最多的20个URL
核心思路是:从 Web 日志(如 Nginx 的 /var/log/nginx/access.log)中筛选过去一小时内日志行,提取 URL 字段(通常是第 7 或第 8 列),去重计数后取 Top 20。
关键难点不在排序,而在「准确截取过去一小时」——awk 本身不擅长时间计算,得靠 date 配合字符串比对。
- 假设日志时间格式为
[12/Jul/2024:15:32:18 +0800](Nginx 默认),需先用date -d '1 hour ago'生成起始时间字符串,再用awk行级匹配 - URL 在日志中位于请求行,即双引号包围的第二字段(
$7对应"GET /path?x=1 HTTP/1.1"),需用match()+substr()提取路径部分,避免把整个请求行当 URL - 别直接用
cut -d' ' -f7—— 若 URL 含空格(如编码后带%20),会切错列
处理时间范围:用 date 生成动态时间戳做字符串前缀匹配
Nginx 日志时间是固定宽度格式([12/Jul/2024:15:32:18),可截取前 17 位(到分钟)做前缀比较,比逐字段解析快得多,也避开了时区转换问题。
实操命令片段:
start_time=$(date -d '1 hour ago' +'%d/%b/%Y:%H:%M')
然后在 awk 中用 >= "[\"$start_time" 判断(注意日志第 4 字段是带方括号的时间字段)。
- 必须加
[前缀,否则01/Jan/2024:00:00会匹配到10/Jan/2024:00:00 - 用
date -d '1 hour ago'而非$(($(date +%s) - 3600)),避免手动转秒再格式化,易出时区或闰秒偏差 - 如果日志用 UTC 时间而服务器本地是 CST,
date -d默认按本地时区解析,需显式加UTC:date -d '1 hour ago UTC'
提取 URL 时过滤掉非 GET/POST 请求和状态码非 2xx 的干扰项
统计「访问频次」通常只关心成功响应的页面请求,忽略 HEAD、OPTIONS、404、500 等噪音数据。
完整管道示例:
awk -v start="[$(date -d '1 hour ago' +'%d/%b/%Y:%H:%M')" '$4 >= start && $9 == "200" && $6 ~ /^"(GET|POST)/ { match($7, /"[^"]+/); url = substr($7, RSTART+1, RLENGTH-1); if (url != "") print url }' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
-
$9 == "200"可换成$9 ~ /^2../匹配所有 2xx 状态码 -
$6 ~ /^"(GET|POST)/确保只统计标准请求方法;若要包含PUT或DELETE,调整正则即可 - URL 提取逻辑
match($7, /"[^"]+/)兼容"GET /api/v1/users HTTP/1.1"和"POST /login?next=%2F HTTP/1.0",不依赖空格分隔
写成可执行脚本时注意日志轮转与权限问题
直接读 /var/log/nginx/access.log 很可能失败:文件权限通常是 root:adm,且生产环境常启用了 logrotate,一小时前的日志可能已压缩或移走。
- 脚本应检查
access.log是否存在,再 fallback 到access.log.1或最近的.gz文件(用zcat处理) - 避免硬编码路径,用
LOG_PATH=${1:-/var/log/nginx/access.log}支持传参 - 若以普通用户运行,需提前配置
sudo权限(如www-data ALL=(ALL) NOPASSWD: /bin/zcat),而不是直接给脚本加 setuid —— 极不安全 - 别忽略
LC_ALL=C环境变量,否则sort | uniq在中文 locale 下可能排序异常或去重失效
最易被忽略的是:Nginx 日志格式可能被自定义过(比如加了 $request_length 或去掉 $http_user_agent),导致 $7 不再是请求行。务必先用 head -1 /var/log/nginx/access.log 确认字段位置。











