awk 命令详解
更新时间:2026-08-28。本文是「一个命令一个文档」系列,聚焦
awk——文本处理三剑客里最"重"的一位(见文本处理命令族)。它和 grep 找、sed 改 互补:awk 负责"算"。
awk 按字段(列)处理文本。它不止能"取某一列",还内置了变量、循环、条件判断和数学运算,本质上是一门小型编程语言。日志统计、报表生成、数据聚合——这些"要对每一行做点计算"的活,awk 一行就能顶一段脚本。
一、最常用:取列
默认以空白(空格或 tab,连续多个当分隔)分列:
awk '{print $1}' file # 打印第 1 列
awk '{print $1, $3}' file # 打印第 1、3 列,逗号输出时空格分隔
awk -F: '{print $1}' /etc/passwd # -F 指定分隔符为冒号
awk -F, '{print $1, $3}' data.csv # CSV 按逗号分几个内置变量要刻进肌肉记忆:
$0是整行,$1~$n是第 n 个字段,NF是这行的总字段数,$NF就是最后一个字段(管它有几列都能取到),NR是当前行号。比如取"倒数第二列"就是$(NF-1),这招在列数不固定时特别救命。
二、指定分隔符与多个分隔符
awk -F'\t' '{print $2}' file # tab 分隔
awk -F'[ :/]' '{print $1, $NF}' file # 用字符集里的任一字符作分隔
-F后面其实是正则。-F'[ :/]'表示空格、冒号、斜杠中任意一个都算分隔符——处理/var/log那种"路径混着时间混着 IP"的杂糅日志时极好用,一行能拆出想要的几段。
三、条件过滤
awk '$3 > 100 {print $1}' file # 第 3 列大于 100 才打印
awk '/error/ {print}' app.log # 含 error 的行(模式匹配)
awk '$0 ~ /timeout/ {print $1}' log # ~ 正则匹配整行
awk '$1 == "nginx" {print}' ps.txt # 字符串相等
awk 'NR > 1 {print}' data.csv # 跳过表头(第 1 行)
awk的过滤语法{ ... }前面可以放条件,满足才执行块。这比"grep 先筛再awk取列"有时更直接。注意字符串比较用==,数值比较用> <,别混——'100' > '20'在字符串比较里是按字典序('1' < '2'),数字比较才是数值,踩过一次就懂。
四、分组计数与聚合:awk 的杀手锏
这是 awk 最高频的用法——"按某列分组统计":
# 按第 1 列分组计数(等价 SQL: SELECT col1, COUNT(*) GROUP BY col1)
awk '{a[$1]++} END {for (k in a) print k, a[k]}' access.log
# 求第 3 列的平均值,跳过表头
awk 'NR>1 {sum+=$3; n++} END {print sum/n}' data.csv
# 按列求和,并格式化输出
awk '{sum[$1]+=$2} END {for (k in sum) printf "%-10s %d\n", k, sum[k]}' sales.txt这个
a[$1]++的写法几乎每周都会用到:用关联数组(哈希)以某列为 key 计数,最后在END块里遍历输出。它一行替代了别人写的几行 Python。日志里"统计每个 IP 的访问次数""统计每种 HTTP 状态码的数量"全是这个套路。
五、BEGIN 与 END 块
awk 'BEGIN {print "开始统计"} {sum+=$1} END {print "合计:", sum}' nums.txt
awk 'BEGIN {FS=":"} {print $1}' /etc/passwd # BEGIN 里设分隔符BEGIN { ... }:在处理任何行之前执行一次,常用来打印表头、初始化变量、设FS(字段分隔符)。END { ... }:在所有行处理完之后执行一次,常用来输出汇总结果。
把
FS放在BEGIN里和在命令行用-F效果一样,但写进脚本文件(用awk -f script.awk)时,放BEGIN里更清晰。
六、实战:Nginx 访问日志按状态码统计
# 第 9 列是状态码,统计各状态码出现次数并排序
awk '{print $9}' access.log | sort | uniq -c | sort -rn
# 等价 awk 原生写法(不依赖 sort/uniq)
awk '{a[$9]++} END {for (k in a) print k, a[k]}' access.log | sort -k2 -rn
# 计算平均响应大小(假设第 10 列是字节数,跳过首行)
awk 'NR>1 {sum+=$10; n++} END {print "avg bytes:", sum/n}' access.log第一条把 sort/uniq 组合(见 grep 实战)和 awk 串起来;第二条则是纯 awk 实现,两条路结果一致,按你顺手选。
七、进阶:几个高频现成模式
这些 awk 写法几乎每周都用到,背下来能省大量时间:
# 1. 求和/求平均某一列(如第 2 列是数值)
awk '{s+=$2} END {print "sum="s, "avg="s/NR}' data.txt
# 2. 按某列分组求最大值(如按用户分组取最大耗时)
awk '{if($3>m[$1]) m[$1]=$3} END {for(k in m) print k, m[k]}' log.txt
# 3. 去重(按某列当 key,只打印首次出现)
awk '!seen[$1]++' file.txt
# 4. 打印倒数第 2 列(不关心总共几列时)
awk '{print $(NF-1)}' file.txt
# 5. 条件着色/标记(超过阈值的行打标记)
awk '$2>100 {print "WARN", $0; next} {print "OK", $0}' metrics.txt第 3 条 !seen[$1]++ 是 awk 去重的经典 idiom:关联数组 seen 第一次见到 $1 时值为 0,!0 为真打印并自增为 1,之后再见就跳过。比 sort -u 灵活(能指定按哪列去重)。
八、和文本三剑客的分工
分工顺序:grep 挑出关心的行 → sed 改写/脱敏 → awk 按列聚合成报表。三者管道接力,一行顶一段 Python。
相关命令
一句话总结
awk = 按字段处理文本的小型语言:$1~$n 取列、NF/$NF/NR 是内置变量、-F 指定分隔符(支持正则)、条件放块前做过滤、a[$1]++ 在 END 里遍历实现分组计数、BEGIN/END 管初始化与汇总;与 grep 找、sed 改 组成文本三剑客,一行顶一段统计脚本,是日志聚合报表的主力。