awk 数组是什么
awk 的数组是关联数组:下标不必是数字,可以是任意字符串,因此 cnt[$1]++ 就能完成按字段分组计数,sum[$2] += $3 就是分组求和。相比 sort 加 uniq 的组合,它一次遍历就能算出多个聚合量,而且不依赖中间排序,特别适合字段多、要同时统计多个维度的场景。
基本信息
| 用途 | 按字段分组计数、求和、求极值,并在 END 块统一输出 |
|---|---|
| 语法 | awk '{cnt[$1]++} END{for (k in cnt) print cnt[k], k}' 文件 |
| 所属 | GNU awk(gawk);POSIX awk 也支持关联数组 |
| 注意 | 遍历顺序是随机的,需要排序时接 sort,或用 gawk 的排序特性 |
典型使用场景
- 按维度计数:状态码、地区、渠道的分布统计
- 分组求和:按客户或商品汇总金额,配合均值与极值一起输出
- 多维统计:同时按两个字段分组,观察交叉分布
- 去重计数:统计唯一值的个数,或找出出现次数超过阈值的项
- 格式化报表:在 END 块里按固定格式输出,直接粘贴到报表或工单
常用示例
常用示例
awk '{cnt[$1]++} END{for (k in cnt) print cnt[k], k}' access.log | sort -rn | head
awk -F, 'NR>1 {sum[$2]+=$3; n[$2]++} END{for (k in sum) printf "%s\t%.2f\t%.2f\n", k, sum[k], sum[k]/n[k]}' sales.csv
awk '{cnt[$1,$9]++} END{for (k in cnt) print cnt[k], k}' access.log | sort -rn | head # 二维下标
awk '{seen[$1]} END{print "unique=" length(seen)}' access.log # 去重计数
awk '{cnt[$1]++} END{for (k in cnt) if (cnt[k]>1000) print k, cnt[k]}' access.log排序与清理
排序与清理
# gawk 可指定遍历顺序,省掉外层 sort
awk 'BEGIN{PROCINFO["sorted_in"]="@ind_str_desc"} {cnt[$1]++} END{for (k in cnt) print cnt[k], k}' access.log | head
# 删除数组元素
awk '{if ($9 ~ /^5/) err[$1]++; else ok[$1]++} END{for (k in err) printf "%s err=%d ok=%d\n", k, err[k], ok[k]+0}' access.log | head常用写法对照
| 写法 | 作用 | 说明 |
|---|---|---|
| cnt[$1]++ | 按字段计数 | 最常用的分组统计形式 |
| sum[$2]+=$3 | 分组求和 | 未出现过的下标默认为 0 |
| cnt[$1,$9]++ | 二维分组 | 用逗号连接两个下标,遍历时同样用逗号取用 |
| length(arr) | 数组长度 | 统计唯一值个数很方便 |
| delete arr[k] | 删除单个元素 | 按条件清理,避免内存膨胀 |
| for (k in arr) | 遍历 | 顺序随机,需要排行就接 sort |
| PROCINFO["sorted_in"] | 指定遍历顺序 | gawk 扩展,可省掉外层排序 |
awk 数组的下标可以用任意字符串拼接,所以还能把多个字段拼成一个键做复合分组;缺点是可读性下降,键里要注意分隔符不要与字段内容冲突。
高基数字段(例如完整 URL 或请求 ID)作为下标时,数组会迅速膨胀,内存占用随唯一值数量线性上升。先规范化(只取路径前缀)或加大统计粒度,可以避免 awk 被内存拖垮。