awk 字段处理与统计¶
awk 适合处理“每行一条记录、每条记录包含若干字段”的文本。基本结构是:
记录和字段¶
默认每行是一条 Record,连续空白分隔字段:
| 变量 | 含义 |
|---|---|
$0 |
当前整行 |
$1、$2 |
第 1、第 2 字段 |
$NF |
最后一个字段 |
NF |
当前行字段数 |
NR |
所有输入累计行号 |
FNR |
当前文件内行号 |
FILENAME |
当前文件名 |
字段分隔符¶
awk -F: '{print $1, $7}' /etc/passwd
awk -F',' 'BEGIN {OFS="\t"} {print $1, $3}' data.csv
awk 'BEGIN {FS="[[:space:]]+"} {print $1}' file
FS 是输入字段分隔符,OFS 是输出字段分隔符;RS/ORS 分别控制记录分隔。普通 awk 不是真正 CSV 解析器:引号中包含逗号、换行或转义时应使用 CSV 工具或语言库。
BEGIN、主体与 END¶
awk '
BEGIN {sum=0}
NR > 1 {sum += $3; count++}
END {
if (count > 0) print "average", sum/count
}
' usage.txt
BEGIN在读取输入前执行,适合初始化和标题。- 主体对每条记录执行。
END在输入结束后执行,适合汇总。
条件、变量和计算¶
awk '$3 >= 90 {print $1, "critical"}' usage.txt
awk '$2 == "ESTAB" {count++} END {print count+0}' connections.txt
awk 'length($0) > 200 {print NR, length($0)}' app.log
字符串比较与数值比较由上下文决定。输入可能包含 %、单位或空值时先清洗和验证,不要把非数字静默当成 0。
数组与分组统计¶
awk 的关联数组非常适合计数:
统计 HTTP 状态码:
awk '{status[$9]++}
END {for (code in status) print code, status[code]}' access.log \
| sort -k2,2nr
for (key in array) 的输出顺序未定义,需要排序就交给 sort,不要依赖 awk 当前碰巧的顺序。
字符串函数¶
awk '{print tolower($1), toupper($2)}' file
awk '{gsub(/timeout/, "TIMEOUT"); print}' app.log
awk '{print substr($1, 1, 10)}' file
awk 'match($0, /request_id=[^ ]+/) {print substr($0, RSTART, RLENGTH)}' app.log
| 函数 | 作用 |
|---|---|
sub() |
替换第一个匹配 |
gsub() |
替换全部匹配 |
split() |
把字符串拆入数组 |
length() |
字符串长度 |
substr() |
截取字符串 |
match() |
查找正则并设置位置变量 |
sprintf() |
格式化为字符串 |
从 Shell 传变量¶
用 -v 传值,不要把 Shell 变量直接拼进 awk 程序。前者能保持引用边界,也更容易读懂。
多文件处理¶
读取第一个文件时 FNR==NR,先建立查找表;next 跳到下一条记录。第二个文件再使用该表。这种写法适合小型关联,文件过大时要考虑内存。
什么时候不用 awk¶
- YAML/JSON/XML:使用结构化解析器。
- 包含复杂引号和换行的 CSV:使用真正 CSV 解析器。
- 需要修改大量文件并处理事务/错误恢复:使用 Python 等脚本语言。
- 日志字段位置不稳定:优先输出 JSON 或明确分隔格式。
官方参考:GNU awk manual。