747 字
约 2 分钟
9
S15-J01 · 文本三剑客:grep / sed / awk 与日志分析

适用读者:看日志全靠肉眼扫的人。
前置:S15-B02。
实测环境:Ubuntu 24.04 / GNU awk(2026-11-05)。
覆盖旧文:—(E 驱动;旧文未覆盖 awk 字段解析)
覆盖扩展:E-S15-004(字段与 NF)、E-S15-005(awk 聚合)。
上一篇:S15-B06 · 时间、时区与系统信息
下一篇:S15-J02 · 定时任务与开机自启。


1. 一句话结论

grep 找行、sed 改行、awk 拆字段。日志分析的关键不是命令,而是"先确认字段编号,再写聚合"。

实测日志(access.log,3 行):

10.0.0.1 - - [05/Nov/2026:10:00:01] "GET /a HTTP/1.1" 200 512
10.0.0.2 - - [05/Nov/2026:10:00:02] "POST /b HTTP/1.1" 500 128
10.0.0.1 - - [05/Nov/2026:10:00:03] "GET /c HTTP/1.1" 404 0

2. 先验字段,再写 awk(E-S15-004)

awk 'NR==1{print "NF="NF; for(i=1;i<=NF;i++) print i": "$i}' access.log

实测输出:

NF=9
1: 10.0.0.1
2: -
3: -
4: [05/Nov/2026:10:00:01]
5: "GET
6: /a
7: HTTP/1.1"
8: 200
9: 512

关键发现:[日期:时间] 中间没有空格 → 只占 1 个字段,所以:

  • $8 = 状态码
  • $9 = 字节数

"为什么不能凭直觉用 $9 当状态码"——我第一次就是这么错的(实测拿到 0/128/512,那是字节)。


3. 三个最常用的分析(实测)

状态码分布:

awk '{c[$8]++} END{for (k in c) print k, c[k]}' access.log | sort
# 200 1
# 404 1
# 500 1

字节总量:

awk '{s+=$9} END{print "total bytes:", s}' access.log
# total bytes: 640

5xx 计数:

awk '$8 ~ /^5/ {n++} END{print n+0}' access.log
# 1

按 IP 聚合排序:

awk '{c[$1]++} END{for (k in c) print c[k], k}' access.log | sort -rn
# 2 10.0.0.1
# 1 10.0.0.2

4. grep 与 sed 的分工(E-S15-005)

需求 命令
过滤含关键字的行 grep -c "ERROR" app.log
上下文 grep -C 3 "Exception" app.log
排除 grep -v "DEBUG"
替换并输出(不改文件) sed 's#/b#/BETA#' f
原地替换 sed -i.bak 's/a/b/' f(先备份)

实测(sed):

sed 's#/b#/BETA#' access.log | grep BETA
→ ... "POST /BETA HTTP/1.1" 500 128

5. 五个误区

误区 真相
"凭直觉用 $9 当状态码" 先 print NF 确认(实测是 $8)
"sed -i 不用备份" 加 .bak 或用 -i.bak
"grep 能统计" 复杂聚合要 awk
"awk 只能处理 CSV" 任意分隔文本(-F)
"日志分析要装工具" 三剑客 + sort 够用

6. 自检

  1. 为什么"先验字段"是 awk 的第一步?
  2. 实测里状态码是第几字段?字节呢?
  3. 统计状态码分布的命令?
  4. grep -C 3 做什么?
  5. sed -i 的风险怎么规避?

7. 下一篇

S15-J02 · 定时任务与开机自启

文本处理讲完,下一篇讲"让脚本按时跑":crontab 的六个坑。


本篇属于系列《S15 · Linux 与运维》。扩展点 E-S15-004/005。全部计数为 Ubuntu 24.04 实测(3 行样例日志)。

S15-J01 · 文本三剑客:grep / sed / awk 与日志分析
https://www.clxhxhhr.top/posts/5395/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。