第 3 章 · 04 按列处理:`awk`


第 3 章 · 04 按列处理:awk

本节摘要:awk 是本章三巨头(grep/sed/awk)里最难、也最强大的一个。它和前两者的根本区别在于:grep 和 sed 的视角是「按行处理字符串」,而 awk 的视角是「按行、按列处理结构化数据」。它本质上是一个「专为表格数据设计的迷你编程语言」——你写一段 awk 脚本,它对输入的每一行执行这段脚本。本节要把 awk 的心智模型讲透:它的「记录/字段」模型(一行是一条记录,按分隔符切成字段 $1/$2),它的「模式-动作」结构(满足某模式的行才执行某动作),以及它的三大场景(打印列、筛选并计算、分组统计)。很多人对 awk 望而生畏,是因为没建立这个心智模型,把它当 grep 那样记选项——一旦你理解了「awk 是一个小型编程语言」,它就从玄学变成了利器。

内容来源:原项目「Linux 命令大全」command/awk.md,精选并套用体系化模板。

学习目标

阅读完本节,你应当能够:

  1. 说清 awk 的工作模型:把输入按行切成「记录」,每行按分隔符切成「字段」($1/$2/.../$0 整行),对每行执行「模式-动作」。
  2. 用 {print $N} 提取指定列,用 -F 指定字段分隔符。
  3. 用「模式」筛选行(awk '$3 > 100 {print $1}'),理解「模式为真才执行动作」。
  4. 用 BEGIN/END 块做初始化与收尾统计(如求总和、平均、计数)。
  5. 写出三大经典场景:打印某列、筛选并计算、按某列分组统计。
  6. 区分 awk 与 grep/sed 的适用场景:结构化按列数据用 awk,纯文本过滤用 grep。

一、设计动机:awk 的心智模型

理解 awk 的关键,是理解它的三个心智模型。

模型 1:记录与字段

awk 把输入看成一张表:每一行是一条「记录」(record),每一行内部按分隔符切成若干「字段」(field)。字段用 $1、$2、$3 访问,$0 表示整行。

假设输入是(ls -l 的输出): -rw-r--r-- 1 root root 4096 Aug 4 file.txt 字段编号: $1 $2 $3 $4 $5 $6 $7 整行即 $0 ​

默认分隔符是「连续的空格或制表符」。如果数据是 CSV(逗号分隔),用 -F',';如果是冒号分隔(/etc/passwd),用 -F':'。

awk '{print $1, $3}' file # 打印第 1、3 字段 awk -F':' '{print $1}' /etc/passwd # 冒号分隔, 打印用户名(第 1 列) awk -F',' '{print $2}' data.csv # 逗号分隔, 打印第 2 列 ​

模型 2:模式-动作

awk 的语法骨架是 模式 { 动作 }。只有当模式为真(即该行满足条件)时,才执行动作。模式可以省略(默认对所有行执行动作),动作也可以省略(默认打印整行)。

awk '{print $1}' file # 无模式: 对所有行打印第 1 列 awk '$3 > 100 {print $1}' file # 有模式: 只对"第3列>100"的行打印第1列 awk '/error/ {print $0}' log # 模式是正则: 含 error 的行打印整行(等价 grep) awk '$3 > 100' file # 无动作: 默认打印整行(满足模式的行) ​

💡 技巧:看 awk 脚本时,先找 { },它前面的是「模式」(条件),里面的是「动作」(干啥)。这个二分法能帮你读懂任何 awk 命令。

模型 3:BEGIN / 主循环 / END

awk 程序的执行顺序是:先执行一次 BEGIN 块,再对每一行执行主块,最后执行一次 END 块。

awk 'BEGIN{print "开始"} {print $1} END{print "结束, 共" NR "行"}' file # └ 程序开始时一次 └ 对每行 └──── 所有行处理完一次 ​

BEGIN 常用于打印表头、初始化变量;END 常用于打印汇总(总和、平均、计数)。两个内置变量最常用:NR(已读记录数,即行号)、NF(当前行的字段数,$NF 即最后一列)。

二、高频组合与实战

场景 1:打印某几列(最常见)

ls -l | awk '{print $1, $9}' # ls -l 输出里取"权限"和"文件名" awk -F':' '{print $1, $7}' /etc/passwd # 用户名 + 默认 shell awk '{print $NF}' file # 打印每行最后一个字段($NF) awk '{print $(NF-1)}' file # 打印倒数第二个字段 ​

场景 2:按某列的值筛选

ps aux | awk '$3 > 10 {print $2, $3, $11}' # CPU 占用>10% 的进程: 打印 PID/CPU%/命令 awk -F',' '$2 > 1000 {print $1}' sales.csv # 销售额>1000 的记录名 awk 'NR==3 {print $0}' file # 只打印第 3 行 awk 'NR>=10 && NR<=20 {print $0}' file # 打印第 10-20 行 ​

场景 3:分组统计(BEGIN/END + 变量)

awk 能在多行之间累积变量,这是它超越 grep/sed 的关键能力——它能「算」。

# 求某列总和 awk '{sum += $1} END {print sum}' nums.txt # 累加每行第 1 列, 最后打印总和 # 统计文件行数(等价 wc -l, 但能配合条件) awk 'END {print NR}' file # 打印总行数 # 按某列分组求和(经典: 按部门统计销售额) # sales.csv 格式: 部门,金额 awk -F',' '{sum[$1] += $2} END {for (k in sum) print k, sum[k]}' sales.csv # 解释: sum 是关联数组, key 是部门, value 是累计金额 # 每行把金额加到对应部门; END 时遍历数组打印 ​

最后这个「按某列分组求和」是 awk 最强大的用法——它用「关联数组」(类似 Python 的 dict)实现了 SQL GROUP BY 的效果,而且只用一行。这是 awk 把命令行从「查找」升级到「分析」的关键。

场景 4:与管道组合的完整实例

# 找出日志里每个错误码出现的次数, 按次数降序(完整管道) grep -oE 'code=[0-9]+' app.log | awk -F'=' '{print $2}' | sort | uniq -c | sort -rn # └ 抽出"code=数字" └────── 取数字 └────── 排序 └ 去重计数 └ 按次数排 # 统计每个接口的平均响应时间(awk 单挑) # log 格式: 接口 耗时ms awk '{count[$1]++; sum[$1] += $2} END {for (k in sum) printf "%s: %.0fms\n", k, sum[k]/count[k]}' access.log ​

三、踩坑与排错

坑 1:字段编号从 1 开始,不是 0

awk '{print $0}' file # $0 是整行 awk '{print $1}' file # $1 是第 1 个字段(不是 $0) ​

这是 awk 与大多数编程语言(数组从 0 开始)的不同之处。$0 被「整行」占用了,字段从 $1 起。

坑 2:分隔符默认是「连续空格」,不是「单个空格」

echo "a b c" | awk '{print $2}' # 输出 b: 连续空格当一个分隔符 echo "a b c" | awk -F' ' '{print $2}' # 输出空: 指定单个空格后, 行为首的两个空格被当作分隔 ​

awk 默认分隔符是「连续的空白字符(空格/制表符)」,这通常正是你想要的。但如果你用 -F' ' 显式指定单个空格,行为会变(它把连续空格当多个分隔符,产生空字段)。通常不需要显式指定 -F' '。

坑 3:引号与 Shell 变量

awk 脚本用单引号包裹,但你想在脚本里用 Shell 变量时,单引号会挡住 $ 展开。两种解法:

# 法 1: 用 -v 把 shell 变量传给 awk 变量(推荐) threshold=100 awk -v t="$threshold" '$3 > t {print $1}' file # 法 2: 用双引号包裹脚本(麻烦, 易错, 不推荐) awk "$3 > $threshold {print \$1}" file ​

坑 4:for (k in sum) 的输出顺序不确定

awk '{sum[$1]++} END {for (k in sum) print k, sum[k]}' file | sort -rn -k2 # └ 要排序就管道给 sort ​

awk 关联数组的遍历顺序不保证固定(通常是哈希序)。要按某列排序,管道给 sort,而不是指望 awk 给你有序输出。

本节要点回顾

  1. awk 的心智模型是「记录-字段」+「模式-动作」+「BEGIN/主/END」三层,理解了它,awk 就从玄学变成利器。
  2. 字段用 $1/$2/.../$NF,整行是 $0,分隔符用 -F 指定(默认连续空白)。
  3. 「模式-动作」:模式 { 动作 },模式为真才执行动作;模式可省略(对所有行),动作可省略(打印整行)。
  4. BEGIN/END 是统计的关键:BEGIN 初始化、END 汇总,中间用变量在多行间累积——这是 awk 能「算」的根本。
  5. 关联数组实现 SQL GROUP BY:{sum[$1]+=$2} END{for(k in sum) print k,sum[k]} 是一行分组统计的经典。
  6. 常见坑:字段从 $1 起($0 是整行)、默认分隔符是连续空白(别显式写 -F' ')、Shell 变量用 -v 传入、数组遍历顺序不确定要靠 sort。

下一节我们补齐「切分、排序与去重」三件套(cut/sort/uniq),把 awk 之外的字段处理工具讲完。至此你已经掌握了三巨头中的两个(grep 已学、awk 刚学),第 03 节的 sed(按行改)和 05 节的 cut/sort/uniq(聚合)加进来,你就集齐了完整的「文本处理流水线」工具箱。


作者与出处
原作者: 灏天文库
来源:jaywcjlove
许可证:MIT
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: 灏天文库 转发
评论区 (0)
U