awk 核心知识点
本指南旨在帮助你通过实践学习 awk 的主要核心功能。
第一步:准备示例文本文件
首先,在你的电脑上创建一个名为 data.txt 的文本文件,内容如下:
ID,Name,Department,Salary,JoinDate
101,Alice,IT,80000,2022-03-15
102,Bob,Sales,65000,2021-11-01
103,Charlie,IT,82000,2023-01-20
104,David,HR,55000,2022-08-10
105,Eve,Sales,70000,2021-12-05
106,Frank,IT,95000,2020-05-30
107,Grace,HR,60000,2023-05-01
108,Heidi,Sales,68000,2022-07-18
文件说明:
- 这是一个简单的 CSV (逗号分隔值) 格式的员工信息表。
 - 第一行是表头。
 - 字段包括:ID, 姓名, 部门, 薪水, 入职日期。
 
第二步:AWK 核心概念回顾
- 基本工作模式: 
awk 'pattern { action }' filenamepattern: 匹配条件,决定是否对当前行执行action。省略则对所有行执行action。action: 一系列操作,通常是处理和打印数据。省略则默认执行print $0(打印整行)。
 - 核心概念:
- 记录 (Record): 
awk处理的基本单位,默认是文件的每一行。用$0表示整个记录。 - 字段 (Field): 记录中被字段分隔符分开的部分。用 
$1,$2,$3, ... 表示。 - 字段分隔符 (FS): 用于分割字段的字符。默认为空格或制表符。可以通过 
-F命令行选项或在BEGIN块中设置FS变量来更改。对于data.txt,我们需要指定-F','。 
 - 记录 (Record): 
 - 常用内置变量:
NR: Number of Record,当前处理的记录(行)的编号,从 1 开始。NF: Number of Fields,当前记录包含的字段数量。FS: Field Separator,输入字段分隔符。OFS: Output Field Separator,输出字段分隔符 (影响print语句中逗号分隔的输出)。
 
第三步:AWK 实践案例 (整合与进阶)
请在你的终端(命令行)中,确保 data.txt 在当前目录,然后逐一执行以下命令并观察结果。
场景 1:基础打印与字段访问
- 
任务:打印所有内容
- 命令 1 (利用默认 action):
awk '1' data.txt - 命令 2 (显式打印整行):
awk '{print $0}' data.txt - 解释: 
1是一个永远为真的pattern,触发默认的print $0动作。第二条命令直接指定action为print $0。 
 - 命令 1 (利用默认 action):
 - 
任务:打印行号 (NR) 和所有内容 ($0)
- 命令:
awk -F',' '{print NR, $0}' data.txt - 解释: 
NR是内置变量,代表当前行号。-F','设置逗号为字段分隔符,虽然这里print $0不直接受影响,但好习惯是处理 CSV 时加上。 
 - 命令:
 - 
任务:打印第 2 列 (姓名) 和第 4 列 (薪水)
- 命令:
awk -F',' '{print $2, $4}' data.txt - 解释: 
-F','告诉awk使用逗号分割字段。$2和$4分别引用第二个和第四个字段。print中的逗号默认输出一个空格 (OFS的默认值)。 
 - 命令:
 
场景 2:根据条件过滤行 (Pattern 的使用)
- 
任务:打印 IT 部门 (第 3 列是 "IT") 的员工姓名和部门
- 命令:
awk -F',' '$3 == "IT" {print $2, $3}' data.txt - 解释: 
pattern是$3 == "IT"。只有当第三个字段精确匹配 "IT" 时,才执行花括号中的action。 
 - 命令:
 - 
任务:打印工资 (第 4 列) 高于 75000 的员工姓名和薪水
- 命令:
awk -F',' '$4 > 75000 {print $2, $4}' data.txt - 解释: 
pattern是$4 > 75000。awk会自动尝试将$4的值作为数字进行比较。 
 - 命令:
 - 
任务:跳过表头 (行号 > 1),打印工资高于 75000 的员工姓名和薪水
- 命令:
awk -F',' 'NR > 1 && $4 > 75000 {print $2, $4}' data.txt - 解释: 
NR > 1是一个额外的条件,确保只处理数据行。&&是逻辑与操作符,两个条件都为真才执行action。 
 - 命令:
 - 
任务:打印 2022 年 (第 5 列包含 "2022") 加入的员工姓名和入职日期
- 命令:
awk -F',' '$5 ~ /2022/ {print $2, $5}' data.txt - 解释: 
pattern是$5 ~ /2022/。~是正则表达式匹配操作符。/2022/是一个正则表达式,匹配任何包含 "2022" 的字符串。 - 思考: 如果只想匹配以 
2022-开头的日期,应使用$5 ~ /^2022-/。 
 - 命令:
 - 
任务:打印 IT 部门 (第 3 列) 且工资大于 80000 (第 4 列) 的员工姓名、部门和薪水
- 命令:
awk -F',' 'NR > 1 && $3 == "IT" && $4 > 80000 {print $2, $3, $4}' data.txt - 解释: 使用 
&&连接多个条件,形成更复杂的pattern。 
 - 命令:
 
场景 3:使用 BEGIN 和 END 块
- 任务:在处理文件前打印标题,处理完所有行后打印结束语
- 命令:
awk -F',' 'BEGIN { print "--- Employee Report Start ---" } { print NR, $0 } END { print "--- Employee Report End ---" }' data.txt - 解释: 
BEGIN块中的代码在读取第一行之前执行一次。END块中的代码在处理完最后一行之后执行一次。中间的{ print NR, $0 }对(满足pattern的)每一行执行。 
 - 命令:
 
场景 4:计算与统计 (结合 BEGIN/END)
- 
任务:计算所有员工 (不含表头) 的工资总和
- 命令:
awk -F',' 'NR > 1 { sum += $4 } END { print "Total Salary:", sum }' data.txt - 解释: 
NR > 1跳过表头。sum += $4对每个员工的薪水进行累加(sum变量会自动初始化为 0)。END块输出最终的总和。 
 - 命令:
 - 
任务:计算所有员工 (不含表头) 的平均工资
- 命令:
awk -F',' 'NR > 1 { sum += $4; count++ } END { if (count > 0) printf "Average Salary: %.2f\n", sum / count; else print "No employee data found."; }' data.txt - 解释: 在处理每行时,同时累加总工资 (
sum) 和员工数量 (count)。END块计算平均值,并使用printf格式化输出(保留两位小数%.2f),同时检查count以避免除零错误。 
 - 命令:
 
场景 5:格式化输出 (printf 和 OFS)
- 
任务:使用
printf格式化打印员工姓名、部门和薪水 (指定对齐和宽度)- 命令:
awk -F',' 'NR > 1 { printf "%-10s %-8s $%d\n", $2, $3, $4 }' data.txt - 解释: 
printf提供更精细的输出控制。%-10s: 输出字符串 (s),左对齐 (-),宽度为 10。%-8s: 输出字符串,左对齐,宽度为 8。$%d: 先输出字面量$,然后输出整数 (d)。\n: 输出换行符。
 
 - 命令:
 - 
任务:添加格式化的表头后打印
- 命令:
awk -F',' 'BEGIN { printf "%-10s %-8s %s\n", "Name", "Dept", "Salary"; printf "--------------------------\n" } NR > 1 { printf "%-10s %-8s $%d\n", $2, $3, $4 }' data.txt - 解释: 在 
BEGIN块中使用printf打印与数据行格式一致的表头和分隔线。 
 - 命令:
 - 
任务:打印姓名、部门和薪水,使用制表符 (
\t) 分隔输出 (使用 OFS)- 命令:
awk -F',' 'BEGIN { OFS="\t" } NR > 1 { print $2, $3, $4 }' data.txt - 解释: 在 
BEGIN块中设置OFS(Output Field Separator) 为制表符\t。当print语句中使用逗号分隔多个参数时,awk会在它们之间插入OFS的值。 
 - 命令:
 
场景 6:使用关联数组进行分组统计
- 
任务:统计各部门的员工人数
- 命令:
awk -F',' 'NR > 1 { dept_count[$3]++ } END { print "--- Department Counts ---"; for (dept_name in dept_count) print dept_name ":", dept_count[dept_name] }' data.txt - 解释:
dept_count[$3]++: 使用部门名称 ($3) 作为关联数组dept_count的索引(键)。每次遇到一个部门的员工,对应键的值就加 1。数组和键会自动创建。END { for (...)... }: 在处理完所有行后,使用for (key in array)循环遍历关联数组dept_count的所有键 (dept_name),并打印每个部门的名称及其统计的人数。
 
 - 命令:
 - 
任务:计算各部门的平均工资
- 命令:
awk -F',' 'NR > 1 { dept_sum[$3] += $4; dept_count[$3]++ } END { print "--- Department Average Salary ---"; for (d in dept_sum) printf "%-8s Avg Salary: %.2f\n", d, dept_sum[d] / dept_count[d] }' data.txt - 解释: 同时使用两个关联数组:
dept_sum按部门累加薪水,dept_count按部门统计人数。END块中遍历其中一个数组(它们的键应该是一样的),计算并格式化输出每个部门的平均工资。 
 - 命令:
 
场景 7:更复杂的逻辑 - 查找极值
- 任务:找出工资最高的员工及其工资
- 命令:
awk -F',' 'NR == 2 { max_salary = $4; name = $2 } NR > 2 { if ($4 > max_salary) { max_salary = $4; name = $2 } } END { print "--- Highest Salary ---"; print "Name:", name; print "Salary:", max_salary }' data.txt - 解释:
NR == 2 { max_salary = $4; name = $2 }: 处理第一个数据行(第二行)时,将其薪水和姓名初始化为当前最高值。NR > 2 { if ($4 > max_salary) ... }: 对于后续的数据行,如果当前行薪水 ($4) 大于已记录的最高薪水 (max_salary),则更新max_salary和对应的姓名name。END { ... }: 输出最终找到的最高工资员工信息。
 - 另一种更简洁的初始化方式(推荐):
这种方式利用了
awk -F',' 'NR > 1 { if ($4 > max_salary || NR == 2) { max_salary = $4; name = $2 } } END { print "--- Highest Salary ---"; print "Name:", name; print "Salary:", max_salary }' data.txtNR == 2这个条件,确保第一次比较时(或者说,在处理第一条数据时)一定会将max_salary和name赋值。 
 - 命令:
 
第四步:总结与后续学习
通过以上实践,你应该掌握了 awk 的以下核心能力:
- 文本行和字段处理: 读取文件,按分隔符切分字段。
 - 条件逻辑 (Pattern): 根据字段内容、行号、正则表达式等条件筛选数据。
 - 数据操作 (Action): 打印、格式化输出、进行数学运算、使用变量。
 - 流程控制: 利用 
BEGIN和END执行初始化和收尾工作。 - 数据聚合: 使用关联数组进行计数、求和、分组统计等。
 - 状态保持: 在处理不同行之间维护信息(如查找最大/最小值)。
 
后续学习建议:
- 深入实践: 修改上述例子,尝试不同的条件、输出格式、计算逻辑。
 - 探索更多内置变量和函数: 如 
FILENAME,FNR,length(),substr(),split(),tolower(),toupper()等。 - 编写 AWK 脚本: 对于复杂的任务,将 
awk命令写入一个.awk文件,使用awk -f script.awk inputfile执行。 - 学习 AWK 流程控制语句: 如 
if-else,while,for(除了for-in数组遍历)。 - 查阅文档: 
man awk是最权威的参考。在线搜索 "awk tutorial", "awk examples", "awk cheat sheet" 等。