人人都会AI编程

9.3 awk:文本处理编程语言

更新时间:2026-07-12

awk 的名字取自其三位创始人 Alfred Aho、Peter Weinberger 和 Brian Kernighan 的姓氏首字母。它虽然常被称为“命令行文本处理工具”,但本质上是一门专为处理结构化文本而设计的解释型编程语言。当需要从日志、CSV、配置文件等文本中提取字段、进行计算、格式化报表或做条件过滤时,awk 往往比 sed 和 grep 更加直接和强大。

1. awk 的工作模型

awk 遵循“记录-字段”模型处理输入:

  • 默认每条记录就是一行,每读取一行就自动将其按分隔符拆分为若干字段
  • 默认的字段分隔符是任意连续空白(空格或 Tab),你也可以通过 -F 参数指定分隔符。
  • 字段按顺序用 $1$2$N 表示,整个当前行用 $0 代表。

awk 程序的结构通常是:

条件 { 动作 }

它会逐行读取输入,对满足“条件”的行执行大括号中的“动作”。缺少“条件”时,则默认对每一行都执行动作;缺少“动作”时,则默认打印整行。

2. 基本语法速查(实用片段)

先看几个最常用的单行命令,感受 awk 的高效:

  • 打印第1列和第3列
  awk '{print $1, $3}' file.txt
  

输出文件每行的第1和第3个字段,用空格隔开。

  • 指定分隔符

对于逗号分隔的 CSV 文件:

  awk -F',' '{print $2}' data.csv
  

冒号分隔的 /etc/passwd

  awk -F: '{print $1, $7}' /etc/passwd   # 用户名和登录 shell
  
  • 按条件过滤

只处理第3列大于 100 的行:

  awk '$3 > 100' file.txt
  

只打印第一列等于 "ERROR" 的行:

  awk '$1 == "ERROR" {print $0}' app.log
  
  • 使用正则匹配

打印包含 “fail” 的行(不区分大小写可借助字符类 [Ff]ailtolower 函数):

  awk '/fail/' logfile
  
  • 统计与计算

累加第2列并打印总和:

  awk '{sum += $2} END {print sum}' data.txt
  

统计行数(即 wc -l 的作用):

  awk 'END {print NR}' file.txt
  

3. 实用场景拆解

下面通过几个典型需求,展示 awk 如何让文本处理变得轻松。

场景一:日志字段提取
Nginx 访问日志通常包含 IP、时间、请求路径、状态码等,用空格分隔。想要输出每个 IP 地址和对应的状态码:

awk '{print $1, $9}' nginx-access.log

如果想只查看 404 错误的 IP:

awk '$9 == 404 {print $1}' nginx-access.log

场景二:计算磁盘使用率
df -h 可能输出带百分比符号的字段,不利于直接运算。用 df -P 结合 awk 可以精确计算使用率:

df -P | awk 'NR>1 {print $1, $5}'   # 打印文件系统及其使用百分比

其中 NR>1 表示跳过第一行标题。

场景三:多文件合并与定制报表
awk 可以同时处理多个文件,内置变量 FILENAME 能标识当前文件名。例如,统计多个日志文件的错误行总数:

awk '/ERROR/ {count++} END {print "Total errors:", count}' /var/log/*.log

场景四:简单格式化输出
awk 内置 printf 函数,可以像 C 语言一样排版。以下命令将文件和大小以整齐的列输出(ls -l 的替代):

ls -l | awk '{printf "%-30s %10s bytes\n", $9, $5}'

4. BEGIN 与 END 块

awk 允许在读取任何数据之前执行 BEGIN 块(如打印表头),在所有输入处理完成后执行 END 块(如打印汇总)。这是生成简单报表的标准模式:

awk 'BEGIN {print "Name\tScore"} 
     {print $1 "\t" $2} 
     END {print "---end---"}' scores.txt

5. 内建变量速记(常用)

  • NR:已读取的总记录数(行数),在多文件处理时依次累加。
  • FNR:当前文件的记录数,每个文件重新计数。
  • NF:当前行的字段数。$NF 就是最后一个字段,$(NF-1) 是倒数第二个。
  • FS:输入字段分隔符,等效于 -F 选项。
  • OFS:输出字段分隔符,默认为空格,可在 BEGIN 中重设。
  • RS:输入记录分隔符,默认为换行符;设为空字符串时以空行分隔。
  • ORS:输出记录分隔符。

例如,把逗号分隔的 CSV 转为冒号分隔:

awk 'BEGIN {FS=","; OFS=":"} {print $1, $2, $3}' data.csv

6. 关联数组与去重

awk 原生支持关联数组(即字典),非常适合做去重和计数。
统计日志中每个 IP 的出现次数:

awk '{ip[$1]++} END {for (i in ip) print i, ip[i]}' access.log

按值降序输出(需要借助管道或 GNU awk 的 asorti 等):

awk '{ip[$1]++} END {for (i in ip) print ip[i], i}' access.log | sort -rn

7. 何时使用 awk

当数据按行列规则排列,且处理逻辑涉及字段级别的判断、运算或格式化时,awk 是最佳选择:

  • 比 sed 更适合处理按字段组织的文本。
  • 比 grep 多了计算和格式化能力。
  • 比写一个完整的 Python 脚本更轻量快捷,尤其适合在管道中作为过滤器。

掌握了 awk,你就拥有了一把在文本流中随时按需切片、统计、变换的“瑞士军刀”。它不会取代通用编程语言,但在命令行下处理结构化文本的效率,是任何其他工具难以匹敌的。