用 Bash 解析 NGINX 与 Apache 访问日志:spike_check 日志汇总脚本实战(Introduction to Bash Scripting 第 20 章)
【免费下载链接】introduction-to-bash-scriptingFree Introduction to Bash Scripting eBook项目地址: https://gitcode.com/GitHub_Trending/in/introduction-to-bash-scripting
本篇技术指南源自开源电子书《Introduction to Bash Scripting》的第 20 章:BASH Script parser to Summarize Your NGINX and Apache Access Logs。当 Linux 服务器出现 CPU 飙升时,访问日志往往是定位问题的第一现场,而面对动辄数 GB 的原始日志,纯手工排查既耗时又易出错。本文将带你用纯 Bash(零额外依赖)编写一个访问日志汇总脚本:统计 GET / POST 请求最多的 Top 20 页面、按请求量排序的 Top 20 IP 及其地理位置,从而快速判断是正常流量、恶意扫描还是暴力破解攻击。读完本文,你将掌握
awk字段提取、sort | uniq -c | sort -rn | head经典管道链、IP 地理定位与 shellcheck 校验的完整实战技能。
为什么需要日志汇总脚本:CPU 飙升排查的第一现场
在生产环境中,我们通常会在第一件事是使用top或htop查看进程列表。如果在进程列表中发现大量 Apache 或 Nginx 进程,下一步就该快速检查访问日志,判断是什么请求导致了 CPU 飙升,或者是否有人在执行恶意操作——例如针对 WordPress 的xmlrpc.php暴力破解。
但直接阅读原始日志常常令人望而生畏:
- 日志文件可能非常庞大,手工翻阅耗时巨大;
- 原始日志格式对经验不足的人并不友好,字段混杂、信息密度低。
正如章节开篇所说,这不是一篇讲解,而是一次挑战:你需要编写一个短小的 Bash 脚本,在不安装任何额外软件的前提下,把整个访问日志汇总成几条可读的结论。
脚本需求:三条核心汇总输出
该章节定义的脚本需要解析并汇总访问日志,输出三类实用信息:
| 汇总项 | 说明 |
|---|---|
| POST 请求最多的 Top 20 页面 | 帮助发现表单滥用、xmlrpc.php暴力破解等异常写入流量 |
| GET 请求最多的 Top 20 页面 | 帮助定位热点资源、抓取行为或异常的高频访问 |
| Top 20 IP 地址及地理位置 | 帮助识别异常地域来源,判断是否需要封禁 |
这三项输出恰好覆盖了日志排查中最常问的三个问题:谁在请求什么、用了什么方法、来自哪里。
前置知识:访问日志的字段结构
要让脚本能解析日志,首先必须理解标准访问日志的字段布局。Apache 与 Nginx 默认都遵循Common Log Format(CLF)或Combined Log Format,一行典型的日志形如:
127.0.0.1 - - [10/Oct/2000:13:55:36 -0700] "GET /index.html HTTP/1.0" 200 2326按空白字符切分后,关键字段在 awk 中的位置如下:
| awk 字段 | 内容 | 示例 |
|---|---|---|
$1 | 客户端 IP 地址 | 127.0.0.1 |
$4、$5 | 请求时间(日期 + 时区) | [10/Oct/2000、13:55:36 -0700] |
$6 | HTTP 方法(带前导引号) | "GET或"POST |
$7 | 请求的 URL 路径 | /index.html |
$9 | 状态码 | 200 |
$10 | 响应字节数 | 2326 |
理解这一布局是编写解析脚本的基础:IP 永远在$1,方法在$6,URL 在$7。这与电子书前面的章节知识一脉相承——如果你还不熟悉字段、参数与条件表达式,可以先温习Bash 变量、Bash 参数与Bash 条件表达式。
核心思路:四段经典管道链
整个汇总脚本的核心,是一条在 Bash 中反复出现的数据处理管道:
awk '{...提取字段...}' 日志文件 | sort | uniq -c | sort -rn | head -20各环节的作用:
awk:按字段切分每一行,只提取目标字段(如$7页面或$1IP),并对方法做条件过滤;sort:将提取出的值排序,使相同值相邻排列;uniq -c:去重并统计每个值的出现次数;sort -rn:按计数(第一列)反向排序,让出现次数最多的排在前面;head -20:截取前 20 行,即“Top 20”。
这是一条标准且高效的日志统计管道,全部由系统自带命令组成,完全符合章节“不安装任何额外软件”的要求。
参考实现:一个可运行的 spike_check 脚本
章节提供了配套的演示脚本(存放于独立的quick_access_logs_summary配套仓库,可在克隆后获得名为spike_check的可执行文件),并鼓励读者先自己动手写,再对照参考实现。下面给出一个严格满足章节三项需求、可直接运行的参考实现,它综合运用了前面章节学到的参数处理、管道与 awk:
#!/bin/bash # spike_check - 汇总 NGINX / Apache 访问日志 # 用法: ./spike_check /path/to/access.log set -euo pipefail LOG_FILE="${1:-}" if [[ -z "$LOG_FILE" ]] || [[ ! -f "$LOG_FILE" ]]; then echo "Usage: $0 /path/to/access.log" exit 1 fi echo "=== Top 20 pages by GET requests ===" awk '$6 ~ /^"GET/ {print $7}' "$LOG_FILE" \ | sort | uniq -c | sort -rn | head -20 echo "" echo "=== Top 20 pages by POST requests ===" awk '$6 ~ /^"POST/ {print $7}' "$LOG_FILE" \ | sort | uniq -c | sort -rn | head -20 echo "" echo "=== Top 20 IP addresses ===" awk '{print $1}' "$LOG_FILE" \ | sort | uniq -c | sort -rn | head -20要点说明:
set -euo pipefail开启严格模式,一旦管道中任一命令失败即退出,避免静默错误——这是调试与测试章节反复强调的实践;"${1:-}"从命令行读取日志路径参数(对应Bash 参数);若未传参或文件不存在,脚本打印用法并退出,而不是产生一堆无意义的空输出;awk '$6 ~ /^"GET/ {print $7}'用正则匹配第六字段是否以"GET开头(注意引号属于字段内容),命中则输出第七字段的 URL;- 三条管道分别输出 GET 页面、POST 页面与 IP 的 Top 20。
如果希望进一步把“IP + 地理位置”合并展示,可以基于 IP 统计结果,再调用公共 IP 地理定位服务(返回 JSON),并用电子书第 18 章介绍的jq解析出国家/地区信息,例如:
awk '{print $1}' "$LOG_FILE" \ | sort | uniq -c | sort -rn | head -20 \ | while read -r count ip; do location=$(curl -s "https://ipinfo.io/$ip/json" | jq -r '.country') printf "%s\t%s\t%s\n" "$count" "$ip" "$location" done需要注意:地理定位需要网络访问,属于可选增强;当日志行数极大时,逐 IP 调用外部服务会有明显耗时,建议只对 Top 20 执行。
运行方式:下载、授权、执行
章节给出的运行流程非常简洁——只要让脚本可执行,然后传入日志路径即可:
chmod +x spike_check ./spike_check /path/to/your/access_log其中/path/to/your/access_log需要替换为实际的日志路径:
- Apache(Ubuntu/Debian 默认路径):
./spike_check /var/log/apache2/access.log- Nginx(默认路径):
./spike_check /var/log/nginx/access.log章节特别强调:该脚本不会对系统做任何修改,它只读取访问日志内容并输出汇总;不过在运行从网络下载的脚本之前,务必先自行审查脚本内容,确认没有恶意代码——这是一条值得固化为习惯的安全准则。
关于脚本体积:日志越大,运行耗时越长,这是正常的。汇总本身就是一次全量扫描,awk逐行处理大文件非常高效,通常远快于人工翻阅。
输出解读:从 Top 列表识别攻击迹象
脚本输出的每行由“出现次数 + 目标”组成,例如:
16 /xmlrpc.php章节用一个真实案例说明了如何解读这类输出:某次运行显示xmlrpc.php收到了16 次 POST 请求。xmlrpc.php是 WordPress 的一个系统文件,经常被攻击者利用来组合不同的用户名与密码进行暴力破解。虽然 16 次请求算不上大规模攻击,但它是一个早期预警信号——据此可以提前采取措施,防止未来演变成更大规模的攻击。
同样的逻辑适用于 IP 地理位置输出:如果汇总结果中出现了一批来自某国家/地区的 IP,而你并不预期有来自该地域的流量,就应当考虑在防火墙或 WAF 层面封禁这些地址。
因此,这份脚本的核心价值在于:把“大海捞针”式的日志排查,压缩为三条 Top 20 列表,让异常在几秒内现形。
与仓库其他内容的衔接:写得更健壮
本仓库不仅包含这一章的讲解,还提供了完整的质量保障机制,可以让你的脚本更接近“生产可用”:
- 仓库的scripts/shellcheck-ebook.sh会从英文版各章节的 markdown 中提取所有 bash 代码块,并对每块执行
shellcheck -S warning静态检查;它对代码片段专门排除了SC2034(变量看似未使用)、SC2154(变量未赋值即引用)、SC2145(参数混合字符串与数组)等教学场景相关的告警。你完全可以仿照这一思路,对自己的spike_check运行shellcheck来发现潜在问题; - 在让脚本全局可用时,可参考创建自定义 Bash 命令,将脚本软链接到
PATH中的目录,之后就能在任何位置直接执行; - 若想对输出做进一步加工(如按状态码过滤、只统计 4xx/5xx),可结合Bash 循环与Bash 条件表达式扩展管道;
- 整个电子书的章节地图见仓库 README.md,本主题属于“真实场景实战”部分,此前的基础章节(变量、参数、数组、循环、函数)是理解本脚本的必要前提。
注意事项与适用前提
- 日志格式差异:上述字段位置基于标准 Combined Log Format。如果你自定义了 log_format(Nginx 常见做法),
$6、$7的语义可能改变,需要同步调整 awk 字段编号; - 大小写与方法匹配:
$6 ~ /^"GET/是大小写敏感匹配,若日志中出现小写方法或额外引号,需视实际情况调整正则; - 地理定位需要联网:Geo 信息来自公共服务,属于可选功能,且应注意服务的使用限制与隐私策略;
- 权限:
/var/log/nginx/access.log等路径通常需要 root 或日志组成员权限才能读取,请以合适用户身份运行。
结论
这是一个典型的“小而美”的 Bash 实战:不依赖任何第三方软件,仅用系统自带的awk、sort、uniq、head管道组合,就完成了一次完整的访问日志体检。它的价值在于两点——速度(几秒内得到全局视图)与洞察(从 Top 列表中快速识别暴力破解与异常地域流量)。
当然,对于关键日志,手工抽查仍然必要;但把日常的“例行检查”自动化,正是 Bash 脚本最值得投入的方向。不妨现在就按本章挑战写一个你自己的版本,再与配套演示脚本对照,把这段经典管道链变成你的肌肉记忆。
【免费下载链接】introduction-to-bash-scriptingFree Introduction to Bash Scripting eBook项目地址: https://gitcode.com/GitHub_Trending/in/introduction-to-bash-scripting
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考