awk 是 Linux 下最强大的文本处理工具之一,名字取自三位创始人 Aho、Weinberger、Kernighan 的姓氏首字母。很多人只用它做简单的列提取,其实 awk 的能力远不止于此。
awk 的核心模型
awk 的工作流程可以概括为:
awk 'pattern { action }' file- pattern:匹配条件(正则、表达式、范围)
- action:执行的操作(打印、计算、变量赋值)
对于每一行,awk 会:
- 自动按分隔符分割字段(默认空格)
- 字段存入
$1, $2, $3...,整行是$0 - 检查 pattern,匹配则执行 action
# 提取第一列和第三列awk'{ print $1, $3 }'data.txt# 只处理包含 "error" 的行awk'/error/ { print $0 }'app.log# 计算文件总行数awk'END { print NR }'data.txtNR是内置变量,表示当前行号(Number of Records)。END是特殊模式,在所有行处理完后执行。
字段分隔符:不只是空格
-F参数指定字段分隔符:
# CSV 文件按逗号分割awk-F',''{ print $1, $3 }'data.csv# 使用正则表达式:一个或多个空格awk-F'[ ]+''{ print $1 }'data.txt# 多字符分隔符awk-F'|''{ print $1 }'data.txt也可以在脚本内设置FS(Field Separator):
awk'BEGIN { FS = "," } { print $1, $3 }'data.csvBEGIN在处理任何行之前执行,常用于初始化变量。
内置变量的秘密
awk 提供了多个内置变量:
| 变量 | 含义 |
|---|---|
$0 | 整行内容 |
$1~$n | 第 n 个字段 |
NF | 当前行字段数(Number of Fields) |
NR | 当前行号(全局) |
FNR | 当前行号(当前文件) |
FS | 字段分隔符 |
OFS | 输出字段分隔符 |
RS | 行分隔符 |
ORS | 输出行分隔符 |
NF的妙用:引用最后一个字段
# 打印每行的最后一个字段awk'{ print $NF }'data.txt# 打印倒数第二个字段awk'{ print $(NF-1) }'data.txt条件判断与循环
awk 支持if-else和for/while循环:
# 按条件过滤并标记awk'{ if ($3 > 100) { print $1, "HIGH" } else { print $1, "NORMAL" } }'data.txt# 计算每行的字段和awk'{ sum = 0 for (i = 1; i <= NF; i++) { sum += $i } print sum }'numbers.txt数组与统计
awk 的数组是关联数组(associative array),键可以是任意字符串:
# 统计每个单词出现次数awk'{ for (i = 1; i <= NF; i++) { count[$i]++ } } END { for (word in count) { print word, count[word] } }'text.txt# 按访问量统计 HTTP 状态码awk'{ count[$9]++ } END { for (code in count) print code, count[code] }'access.log这段代码中$9是 Nginx 日志的状态码字段(假设标准格式)。
实战案例:分析 Nginx 访问日志
假设日志格式:
192.168.1.1 - - [10/May/2026:10:30:45 +0800] "GET /api/users HTTP/1.1" 200 1234 "-" "Mozilla/5.0"1. 统计 Top 10 访问 IP
awk'{ print $1 }'access.log|sort|uniq-c|sort-rn|head-10纯 awk 实现:
awk'{ ip[$1]++ } END { for (i in ip) print ip[i], i }'access.log|sort-rn|head-102. 计算平均响应时间
假设日志格式包含响应时间(最后一个字段):
awk'{ total += $NF count++ } END { print "Average:", total/count, "ms" }'access.log3. 提取 4xx 和 5xx 错误
# 提取所有 4xx 和 5xx 状态码的请求awk'$9 ~ /^[45][0-9][0-9]$/ { print $0 }'access.log# 统计错误类型分布awk'$9 ~ /^[45][0-9][0-9]$/ { errors[$9]++ } END { for (code in errors) print code, errors[code] }'access.log~是正则匹配操作符,$9 ~ /^.../表示第 9 个字段匹配正则。
性能优化技巧
1. 跳过无效行
用next跳过不需要处理的行:
awk'/^#/ { next } { print $1 }'config.conf跳过注释行(以#开头)。
2. 只处理前 N 行
awk'NR > 100 { exit } { print $1 }'data.txt处理前 100 行后退出,避免读取整个大文件。
3. 多文件处理时的 FNR
当处理多个文件时,NR是全局行号,FNR是当前文件行号:
# 每个文件单独统计awk'FNR == 1 { print "File:", FILENAME } { print NR, FNR, $0 }'file1.txt file2.txt复杂案例:计算移动平均
假设有一个温度数据文件,每行一个温度值,计算 3 点移动平均:
awk'{ values[NR] = $1 if (NR >= 3) { sum = values[NR] + values[NR-1] + values[NR-2] print (NR-2), sum/3 } }'temperature.txtawk vs sed vs grep
很多人分不清这三个工具的边界:
| 工具 | 核心能力 | 典型场景 |
|---|---|---|
| grep | 行过滤 | 快速搜索匹配行 |
| sed | 流编辑 | 替换、删除、插入 |
| awk | 字段处理 + 计算 | 统计、报表、格式化 |
三者常组合使用:
# 组合示例:提取 error 行,替换时间戳格式,统计按小时分布grep"ERROR"app.log|\sed's/\[.*\]//'|\awk'{ count[$1]++ } END { for (h in count) print h, count[h] }'小结
awk 的强大在于:
- 自动字段分割,省去手动 split
- 完整的编程语言(变量、数组、函数、循环)
- 内置的模式匹配机制
掌握 awk,处理文本文件就像用 SQL 查询数据库一样高效。复杂的统计、格式化、转换任务,一行 awk 命令就能搞定。
相关工具:Linux sed 命令 | 文本去重工具 | Grep 命令详解