4. awk 入门:字段分割、内置变量、基本模式与动作
说真的,在接触 HSM 日志之前,我对 awk 的认知仅限于“一个能把文本切成列的工具”。直到有一次,我需要从几十万行 HSM 审计日志里提取操作类型、用户 ID 和时间戳,还得算出每种操作的出现次数。用 Python 写脚本?太慢了。用 grep 加 cut?碰到字段顺序乱掉的情况就崩了。
后来我花了一个下午老老实实啃 awk,才发现它天生就是干这个活的。嗯,这一章我们就来聊聊 awk 最核心的三个能力:怎么切字段、有哪些内置变量、以及怎么用模式和动作来过滤和变换数据。
4.1 字段分割:awk 的核心本领
awk 默认把一行文本按空格或制表符分割成多个字段。字段编号从 $1 开始,$2 是第二个字段,$0 代表整行。你想想看,这比手写 cut -d' ' 灵活太多了。
举个例子,假设 HSM 日志里有一行:
2025-03-21 14:30:22 HSM_1 key_generate user_admin success
用 awk 打印时间戳和操作类型:
awk '{print $2, $3}' hsm.log
$2 是时间,$3 是 HSM 设备名,$4 是操作。输出结果像这样:
14:30:22 key_generate
{print NF} 看一眼当前行有几个字段,确认不会把 $5 写成 $6。特别是日志格式突然多了个字段的情况,很容易踩坑。
如果字段之间的分隔符不是空格呢?比如 HSM 的 CSV 格式日志,字段用逗号隔开。这时候就用 -F 指定分隔符:
awk -F',' '{print $1, $3}' hsm.csv
更复杂的情况也用得到——日志里字段之间可能有多个空格,或者制表符和空格混用。awk 默认会把连续的空格/制表符当作一个分隔符处理,这比用 cut -d' ' 舒服多了。我在项目里遇到过日志格式不规范,字段间距不统一,用 cut 解析出来的数据全是错的,换成 awk 就没问题了。
| 分隔符类型 | 命令行写法 | 示例 |
|---|---|---|
| 空白(默认) | awk '{print $1}' |
连续空格/制表符自动合并 |
| 逗号 | awk -F',' '{print $2}' |
CSV 文件专用 |
| 冒号 | awk -F':' '{print $1}' |
类似 /etc/passwd 的格式 |
| 正则表达式 | awk -F'[,;]' '{print $1}' |
逗号或分号都算分隔符 |
4.2 内置变量:awk 自带的信息牌
awk 预设了一些变量,能直接告诉你当前处理到第几行、总共有多少字段、当前行是什么——不用你自己去计数。我最常用的四个是 NR、NF、FS 和 OFS。
- NR:当前已读取的记录(行)数。
- NF:当前行的字段数量。
- FS:输入字段分隔符,默认是空格/制表符。
- OFS:输出字段分隔符,默认是空格。
我来给你看个实际场景。统计 HSM 日志中操作失败的次数:
awk '$NF == "fail" {count++} END {print "Fail count:", count}' hsm.log
这里的 $NF 用了嵌套写法——先拿 NF 得到最后一个字段的位置,再通过 $ 引用那个字段。这种用法在处理固定列数但列名不固定时特别有用。
FS 赋值时,如果写 FS="," 但后面又换了 FS=":",awk 不会自动重新解析已经读过的行。只有新读入的行才会用新的分隔符。所以如果要动态改变分隔符,记得在读取新行之前 next 或者重新打开文件。
输出字段分隔符 OFS 常用在重构输出格式。比如原始日志是逗号分隔,但你想输出成制表符分开的表格:
awk 'BEGIN{FS=","; OFS="\t"} {print $1, $3, $5}' hsm.csv
这样一来,输出的字段之间就用制表符隔开了,复制到 Excel 里直接能识别成列。我在做日志报告时经常这么干,比用手动替换快得多。
4.3 基本模式与动作:让 awk 只处理你关心的行
awk 的基本语法是 模式 { 动作 }。模式控制哪些行被执行动作,动作就是你要对匹配到的行做什么。
没有模式时,所有行都会执行动作,比如前面的 {print $1}。加上模式后,awk 就变成了一个“带条件的过滤器”。
模式有很多种,最常见的两种是:
- 行范围模式:按行号匹配,如
NR >= 10 && NR <= 20。 - 正则表达式模式:按内容匹配,如
/key_generate|sign/。
举个例子,只处理第 100 到第 200 行之间的日志,并且只打印包含“fail”的行:
awk 'NR >= 100 && NR <= 200' hsm.log | awk '/fail/ {print $0}'
当然你也可以写在一个 awk 里:
awk 'NR >= 100 && NR <= 200 && /fail/ {print NR, $0}' hsm.log
这样输出的每一行前面都会带上行号,方便你定位到出错的那条日志。我记得有一次系统告警说 HSM 密钥生成失败,我从几万行日志里用这个命令五分钟就定位到了问题行。
再看一个更实用的场景:对特定操作类型统计次数。HSM 日志里有 key_generate、sign、verify 三种操作,我只想统计 sign 和 verify 的出错次数:
awk '/sign|verify/ && /fail/ {count++} END {print "Sign/Verify fails:", count}' hsm.log
模式动作还有一个经典用法——BEGIN 和 END 两个特殊模式。BEGIN 在读取任何行之前执行,适合初始化变量、设置表头;END 在所有行处理完成后执行,适合输出汇总结果。前面统计 fail 次数的例子里,END 就派上了用场。
4.4 本章小结
awk 入门其实就三件事:知道怎么切字段($1、$NF、-F),记住内置变量(NR、NF、FS、OFS),学会组合模式与动作。这三样东西加起来,就能应付 HSM 日志解析中绝大部分的数据提取和统计需求了。
你想想看,以前用 grep+cut+wc 组合拳才能做的事,现在一条 awk 命令搞定。而且 awk 脚本可以写在文件里复用,不会因为键盘敲错一个管道就全盘重来。