跳转至

awk 字段处理与统计

awk 适合处理“每行一条记录、每条记录包含若干字段”的文本。基本结构是:

awk '条件 { 动作 }' 文件
awk '{print $1}' file
awk '$3 > 80 {print $1, $3}' usage.txt
awk '/ERROR/ {print NR, $0}' app.log

记录和字段

默认每行是一条 Record,连续空白分隔字段:

变量 含义
$0 当前整行
$1$2 第 1、第 2 字段
$NF 最后一个字段
NF 当前行字段数
NR 所有输入累计行号
FNR 当前文件内行号
FILENAME 当前文件名
awk '{print NR, NF, $1, $NF}' file
awk 'NF >= 4 {print $2}' file

字段分隔符

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。

df -P | awk 'NR>1 {
  value=$5
  sub(/%$/, "", value)
  if (value >= 80) print $6, value "%"
}'

数组与分组统计

awk 的关联数组非常适合计数:

awk '{count[$1]++}
     END {for (key in count) print count[key], key}' access.log \
  | sort -nr

统计 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 传变量

threshold=80
awk -v limit="$threshold" '$3 >= limit {print $1, $3}' usage.txt

-v 传值,不要把 Shell 变量直接拼进 awk 程序。前者能保持引用边界,也更容易读懂。

多文件处理

awk 'FNR==NR {owner[$1]=$2; next}
     {print $1, owner[$1], $3}' owners.txt usage.txt

读取第一个文件时 FNR==NR,先建立查找表;next 跳到下一条记录。第二个文件再使用该表。这种写法适合小型关联,文件过大时要考虑内存。

什么时候不用 awk

  • YAML/JSON/XML:使用结构化解析器。
  • 包含复杂引号和换行的 CSV:使用真正 CSV 解析器。
  • 需要修改大量文件并处理事务/错误恢复:使用 Python 等脚本语言。
  • 日志字段位置不稳定:优先输出 JSON 或明确分隔格式。

官方参考:GNU awk manual