​awk​ 是一种强大的文本处理工具,它不仅仅是简单的命令行工具,更是一门数据驱动的编程语言。其名称取自三位创始人 Alfred Aho、Peter Weinberger 和 Brian Kernighan 的姓氏首字母。awk​ 擅长对文本文件进行模式扫描、字段分割、数据过滤、格式化输出等操作,是 Linux 文本处理三剑客(grep​、sed​、awk​)之一。


1. 基本语法

awk [选项] '模式{动作}' 文件...
  • ​模式​:决定何时执行动作,可以是正则表达式、关系表达式、范围模式等。省略模式则对每一行执行动作。

  • ​动作​:用大括号括起来的一组语句,如 print​、printf​、赋值、循环等。

  • ​选项​:常见的有 -F​(指定字段分隔符)、-v​(定义变量)、-f​(从脚本文件读取 awk 程序)等。

示例:打印 /etc/passwd​ 的第一列(用户名)

awk -F: '{print $1}' /etc/passwd

2. 工作流程

  1. 自动从输入文件(或标准输入)读取一行。

  2. 按分隔符将行分割成字段,$1​、$2​... $NF​ 分别代表第1、2...最后一个字段,$0​ 代表整行。

  3. 依次对每一行匹配模式,若匹配则执行动作。

  4. 处理完所有行后,可执行 END​ 块中的动作。


3. 常用选项详解

选项

说明

​-F fs​

指定输入字段分隔符(可以是正则表达式),如 -F:​ 表示冒号分隔。

​-v var=value​

在程序执行前设置变量,常用于传递外部 Shell 变量。

​-f scriptfile​

从文件中读取 awk 程序,适用于复杂脚本。

​-W​

兼容性或调试选项(如 -W dump-variables​)。


4. 内置变量

变量

含义

​NF​

当前行的字段数(Number of Fields)。

​NR​

当前记录号(行号,从1开始)。

​FNR​

当前文件中的记录号(每个文件独立计数)。

​FS​

输入字段分隔符,默认为空白(空格或Tab)。可通过 -F​ 或赋值改变。

​OFS​

输出字段分隔符,默认为空格。

​RS​

输入记录分隔符,默认为换行符。

​ORS​

输出记录分隔符,默认为换行符。

​FILENAME​

当前输入文件的名称。

​ARGC​ / ARGV​

命令行参数个数和参数数组。

示例:打印行号、字段数、第一个和最后一个字段

awk '{print NR, NF, $1, $NF}' file.txt

5. 模式和动作

5.1 模式类型

  • ​空模式​:匹配所有行。

  • ​正则表达式​:/正则/​,如 /^root/​ 匹配以 root 开头的行。

  • ​关系表达式​:NR==10​(第10行)、$3 > 100​(第3字段大于100)。

  • ​范围模式​:pattern1, pattern2​ 从匹配 pattern1 的行开始到匹配 pattern2 的行结束。

  • ​BEGIN / END​:BEGIN​ 在处理任何输入之前执行一次;END​ 在处理完所有输入之后执行一次。

示例:处理第 5 到第 10 行

awk 'NR>=5 && NR<=10 {print $0}' file.txt

或使用范围模式:

awk 'NR==5,NR==10' file.txt

5.2 动作

动作包含在 {}​ 中,支持:

  • 变量赋值与运算

  • 打印(print​、printf​)

  • 条件判断(if​、else​)

  • 循环(while​、for​、do while​)

  • 数组操作

  • 函数调用

示例:根据第二列数值输出不同信息

awk '$2 > 80 {print $1, "优秀"} $2 >= 60 && $2 <= 80 {print $1, "及格"} $2 < 60 {print $1, "不及格"}' scores.txt

6. 字段处理

6.1 自定义分隔符

  • 使用 -F​ 选项:awk -F: '{print $1}' /etc/passwd​

  • 在 BEGIN 中设置 FS​:awk 'BEGIN{FS=":"} {print $1}'​

  • 支持正则表达式分隔符:awk -F'[ :\t]+' '{print $1}'​

6.2 字段引用与修改

字段可以直接赋值,如 $2 = "newvalue"​,此时 $0​ 会使用 OFS​ 重新构建。

6.3 字段数量与最后一个字段

  • ​NF​:字段总数

  • ​$NF​:最后一个字段

  • ​$(NF-1)​:倒数第二个字段


7. 控制语句

语句

示例

​if (条件) {动作}​

​if ($1 ~ /^A/) print $1​

​if...else...​

​if ($3>0) sum+=$3 else count++​

​while (条件) {动作}​

​while (i<=NF) {print $i; i++}​

​for (初始化;条件;增量)​

​for (i=1;i<=NF;i++) sum+=$i​

​for (var in array)​

​for (key in arr) print key, arr[key]​

​break​ / continue​

循环控制

​exit​

退出 awk 程序,可设定返回值

示例:计算每行数值之和

awk '{sum=0; for(i=1;i<=NF;i++) sum+=$i; print sum}' numbers.txt

8. 数组

awk 支持关联数组(即下标可以是任意字符串)。

  • ​创建与赋值​:arr["key"] = value​

  • ​遍历​:for (k in arr) { print k, arr[k] }​(遍历顺序不确定)

  • ​删除​:delete arr[key]​ 或 delete arr​(删除整个数组)

常见用法:统计单词出现次数

awk '{for(i=1;i<=NF;i++) count[$i]++} END{for(w in count) print w, count[w]}' file.txt

9. 内置函数

9.1 字符串函数

函数

说明

​length([s])​

返回字符串 s 的长度,若不提供则返回 $0​ 长度。

​tolower(s)​

转换为小写。

​toupper(s)​

转换为大写。

​index(s, t)​

返回子串 t 在 s 中的位置(从1开始)。

​substr(s, p [, n])​

从 s 的 p 位置开始取 n 个字符。

​split(s, a [, sep])​

将 s 按分隔符 sep 分割存入数组 a,返回元素个数。

​gsub(r, s [, t])​

将 t 中所有匹配正则 r 的字符串替换为 s,返回替换次数。

​sub(r, s [, t])​

将 t 中第一个匹配正则 r 的字符串替换为 s。

​match(s, r [, a])​

在 s 中匹配正则 r,返回位置,并可通过 RSTART​、RLENGTH​ 获取信息。

9.2 数学函数

函数

说明

​int(x)​

取整。

​sqrt(x)​

平方根。

​rand()​ / srand([x])​

随机数。

​sin(x)​ / cos(x)​

三角函数。

9.3 时间函数

  • ​systime()​:返回当前系统时间戳(秒)。

  • ​strftime([format [, timestamp]])​:格式化时间,如 strftime("%Y-%m-%d")​。

示例:打印当前日期

awk 'BEGIN{print strftime("%Y-%m-%d %H:%M:%S")}'

10. 自定义函数

用户可定义函数,语法:

function 函数名(参数列表) {
    语句
    return 返回值
}

函数定义通常放在 BEGIN​ 之前或单独文件。示例:

awk '
function max(a,b) {
    return a>b ? a : b
}
{print max($1,$2)}' file.txt

11. 典型示例

11.1 格式化输出

使用 printf​ 控制格式,类似 C 语言:

awk '{printf "%-10s %5d\n", $1, $2}' data.txt

11.2 多文件处理

​NR​ 总行号,FNR​ 当前文件行号,判断当前处理的文件:

awk 'FNR==1{print "File: " FILENAME} {print $0}' file1 file2

11.3 条件统计

统计每列的和:

awk '{for(i=1;i<=NF;i++) sum[i]+=$i} END{for(i=1;i<=NF;i++) print "col" i ":", sum[i]}' data.txt

11.4 去除重复行(保持顺序)

awk '!seen[$0]++' file.txt

11.5 合并行

将两行合并为一行(例如每隔一行拼接):

awk 'NR%2==1{line=$0; next} {print line, $0}' file.txt

11.6 处理 CSV 文件

假设 CSV 用逗号分隔,但字段内可能含有引号。简单的逗号分割:

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

更复杂的 CSV 解析可使用 FPAT​ 变量(GNU awk):

bash

awk 'BEGIN{FPAT="([^,]+)|(\"[^\"]+\")"} {print $1}' data.csv

11.7 调用外部命令

使用 system()​ 函数或管道:

awk '{cmd="echo " $1; system(cmd)}' file.txt

或通过 |​ 将数据发送给外部命令:

awk '{print $1 | "sort"}' file.txt

12. 高级技巧

12.1 多字符分隔符

awk -F'[:;]' '{print $1}' file   # 分隔符可以是冒号或分号

12.2 忽略大小写

设置 IGNORECASE=1​:

awk 'BEGIN{IGNORECASE=1} /root/ {print}' /etc/passwd

12.3 输出到多个文件

awk '{print > ($1 > 100 ? "high.txt" : "low.txt")}' data.txt

12.4 使用 getline

​getline​ 可读取下一行或从其他文件读取,需谨慎使用:

awk '{if(getline > 0) print $0, "next line:", $0}' file.txt

12.5 调试

  • 打印所有内置变量:awk '{print "NR="NR, "NF="NF, "$0=" $0}' file​

  • 使用 -W dump-variables​(gawk)在程序结束时打印变量值。


13. 不同 awk 变体

  • ​awk​:经典版本,如 BSD awk。

  • ​nawk​:新的 awk(New awk),功能增强。

  • ​gawk​:GNU awk,功能最丰富,支持更多扩展(如 FPAT​、gensub​、asort​、网络等)。Linux 默认通常是 gawk​,调用 awk​ 命令时实际是 gawk​。

检查版本:awk --version​


14. 总结

​awk​ 是 Linux 下极为灵活的数据提取和转换工具,掌握它的核心是理解“模式-动作”机制,熟练使用内置变量和函数,并能灵活运用数组和控制流。对于日常文本分析、日志处理、报表生成等任务,awk​ 往往能一行解决问题,极大提高效率。

建议初学者从简单字段提取开始,逐步学习条件判断、统计汇总、多文件处理,再深入数组和函数。多实践是掌握 awk​ 的最佳途径。