- 先筛选出最近 1 小时的日志行
- 再用
awk提取 IP 并统计
由于 Nginx 默认日志时间格式为:[14/Jan/2026:21:34:12 +0800](注意:当前系统时间是 2026-01-14 21:34)
推荐方案:使用 awk + 时间比较(精确、高效)
# 获取当前时间(用于计算1小时前)
now=$(date '+%s')
one_hour_ago=$((now - 3600))
awk -v cutoff="$one_hour_ago" '
{
# 提取时间字段:第4列,格式如 [14/Jan/2026:21:34:12
time_str = $4
gsub(/\[/, "", time_str) # 去掉 [
# 转换为时间戳(GNU awk 支持 mktime)
split(time_str, parts, /[:/\]/)
day = parts[1]
month = parts[2]
year = parts[3]
hour = parts[4]
minute = parts[5]
second = parts[6]
# 月份转数字
months["Jan"] = "01"; months["Feb"] = "02"; months["Mar"] = "03"
months["Apr"] = "04"; months["May"] = "05"; months["Jun"] = "06"
months["Jul"] = "07"; months["Aug"] = "08"; months["Sep"] = "09"
months["Oct"] = "10"; months["Nov"] = "11"; months["Dec"] = "12"
mon_num = months[month]
if (mon_num == "") next
# 构造 ISO 时间字符串并转时间戳
cmd = "date -d \"" year "-" mon_num "-" day " " hour ":" minute ":" second "\" +%s 2>/dev/null"
cmd | getline log_time
close(cmd)
if (log_time >= cutoff) {
print $1
}
}
' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -10
⚠️ 缺点:每行都调用
date命令,大日志文件会很慢。
更高效方案:用 date 生成时间范围 + grep 初筛(推荐)
步骤:
- 生成 1 小时前的时间字符串(如
[14/Jan/2026:20:) - 用
awk或sed提取从该时间开始的所有日志 - 再统计 IP
# 1. 获取1小时前的“日/月/年:小时”格式(用于匹配日志)
start_time=$(date -d '1 hour ago' '+%d/%b/%Y:%H')
# 2. 提取从 start_time 开始的所有日志行(包含之后所有分钟)
awk -v start="$start_time" '
$0 ~ "\\[" start {
in_range = 1
}
in_range {
print $1
}
' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -10
原理:
- Nginx 日志按时间顺序写入(假设未 rotate)
- 一旦匹配到
[14/Jan/2026:20,后面所有行都是 ≥ 20:00 的 - 所以从第一个匹配行开始,后续全部属于最近1小时
✅ 优点:不解析每行时间,速度极快,适合大日志
❌ 缺点:如果日志不是严格按时间排序(如多 worker 写入乱序),可能不准(但 Nginx 默认是顺序的)
最佳实践(兼顾准确与性能)
如果你的日志是标准格式且按时间排序(绝大多数情况),用下面这个 简洁高效版:
# 获取1小时前的时间前缀,例如 "14/Jan/2026:20"
prefix=$(date -d '1 hour ago' '+%d/%b/%Y:%H')
# 从匹配该前缀的第一行开始,提取所有IP并统计
awk -v p="$prefix" '
match($0, /\[([^\]]+)/, m) {
if (m[1] >= p ":00") {
print $1
}
}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -10
💡
m[1] >= p ":00"确保时间字符串比较正确(如14/Jan/2026:20:30>14/Jan/2026:20:00)
补充:处理日志轮转(access.log.1, .gz 等)
如果需要包含压缩日志(如 logrotate 后的):
# 合并当前 + 上一个日志
zcat -f /var/log/nginx/access.log* | awk -v p="$prefix" '
match($0, /\[([^\]]+)/, m) {
if (m[1] >= p ":00") {
print $1
}
}' | sort | uniq -c | sort -nr | head -10
zcat -f可同时处理.gz和未压缩文件
总结:推荐命令(直接复制使用)
prefix=$(date -d '1 hour ago' '+%d/%b/%Y:%H')
awk -v p="$prefix" '
match($0, /\[([^\]]+)/, m) {
if (m[1] >= p ":00") {
print $1
}
}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -10