Linux awk 命令:文本处理的瑞士军刀

awk 是 Linux 下最强大的文本处理工具之一,名字取自三位创始人 Aho、Weinberger、Kernighan 的姓氏首字母。很多人只用它做简单的列提取,其实 awk 的能力远不止于此。

awk 的核心模型

awk 的工作流程可以概括为:

复制代码
awk 'pattern { action }' file
  • pattern:匹配条件(正则、表达式、范围)
  • action:执行的操作(打印、计算、变量赋值)

对于每一行,awk 会:

  1. 自动按分隔符分割字段(默认空格)
  2. 字段存入 $1, $2, $3...,整行是 $0
  3. 检查 pattern,匹配则执行 action
bash 复制代码
# 提取第一列和第三列
awk '{ print $1, $3 }' data.txt

# 只处理包含 "error" 的行
awk '/error/ { print $0 }' app.log

# 计算文件总行数
awk 'END { print NR }' data.txt

NR 是内置变量,表示当前行号(Number of Records)。END 是特殊模式,在所有行处理完后执行。

字段分隔符:不只是空格

-F 参数指定字段分隔符:

bash 复制代码
# CSV 文件按逗号分割
awk -F',' '{ print $1, $3 }' data.csv

# 使用正则表达式:一个或多个空格
awk -F'[ ]+' '{ print $1 }' data.txt

# 多字符分隔符
awk -F'|' '{ print $1 }' data.txt

也可以在脚本内设置 FS(Field Separator):

bash 复制代码
awk 'BEGIN { FS = "," } { print $1, $3 }' data.csv

BEGIN 在处理任何行之前执行,常用于初始化变量。

内置变量的秘密

awk 提供了多个内置变量:

变量 含义
$0 整行内容
$1~$n 第 n 个字段
NF 当前行字段数(Number of Fields)
NR 当前行号(全局)
FNR 当前行号(当前文件)
FS 字段分隔符
OFS 输出字段分隔符
RS 行分隔符
ORS 输出行分隔符

NF 的妙用:引用最后一个字段

bash 复制代码
# 打印每行的最后一个字段
awk '{ print $NF }' data.txt

# 打印倒数第二个字段
awk '{ print $(NF-1) }' data.txt

条件判断与循环

awk 支持 if-elsefor/while 循环:

bash 复制代码
# 按条件过滤并标记
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),键可以是任意字符串:

bash 复制代码
# 统计每个单词出现次数
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

bash 复制代码
awk '{ print $1 }' access.log | sort | uniq -c | sort -rn | head -10

纯 awk 实现:

bash 复制代码
awk '{
  ip[$1]++
}
END {
  for (i in ip) print ip[i], i
}' access.log | sort -rn | head -10

2. 计算平均响应时间

假设日志格式包含响应时间(最后一个字段):

bash 复制代码
awk '{
  total += $NF
  count++
}
END {
  print "Average:", total/count, "ms"
}' access.log

3. 提取 4xx 和 5xx 错误

bash 复制代码
# 提取所有 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 跳过不需要处理的行:

bash 复制代码
awk '/^#/ { next } { print $1 }' config.conf

跳过注释行(以 # 开头)。

2. 只处理前 N 行

bash 复制代码
awk 'NR > 100 { exit } { print $1 }' data.txt

处理前 100 行后退出,避免读取整个大文件。

3. 多文件处理时的 FNR

当处理多个文件时,NR 是全局行号,FNR 是当前文件行号:

bash 复制代码
# 每个文件单独统计
awk 'FNR == 1 { print "File:", FILENAME } { print NR, FNR, $0 }' file1.txt file2.txt

复杂案例:计算移动平均

假设有一个温度数据文件,每行一个温度值,计算 3 点移动平均:

bash 复制代码
awk '{
  values[NR] = $1
  if (NR >= 3) {
    sum = values[NR] + values[NR-1] + values[NR-2]
    print (NR-2), sum/3
  }
}' temperature.txt

awk vs sed vs grep

很多人分不清这三个工具的边界:

工具 核心能力 典型场景
grep 行过滤 快速搜索匹配行
sed 流编辑 替换、删除、插入
awk 字段处理 + 计算 统计、报表、格式化

三者常组合使用:

bash 复制代码
# 组合示例:提取 error 行,替换时间戳格式,统计按小时分布
grep "ERROR" app.log | \
  sed 's/\[.*\]//' | \
  awk '{ count[$1]++ } END { for (h in count) print h, count[h] }'

小结

awk 的强大在于:

  1. 自动字段分割,省去手动 split
  2. 完整的编程语言(变量、数组、函数、循环)
  3. 内置的模式匹配机制

掌握 awk,处理文本文件就像用 SQL 查询数据库一样高效。复杂的统计、格式化、转换任务,一行 awk 命令就能搞定。


相关工具:Linux sed 命令 | 文本去重工具 | Grep 命令详解

相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式