跳转到内容

awk

awk 是一种用于文本和数据处理的编程语言。数据可以来自标准输入、文件或其他命令的输出。awk 提供变量、数学运算、字符串处理等编程语言功能,特别适用于按列(字段)分析和处理结构化文本(如 CSV、日志文件、配置文件等)

Terminal window
awk [options] 'pattern {action}' file
  • options:用于控制 awk 行为的选项
  • pattern:用于匹配输入数据的模式(可以是正则表达式、条件表达式等)
  • action:当该行匹配模式时执行的操作(用花括号 {} 括起来)

如果省略 pattern,表示对所有行都执行 如果省略 action,默认是打印整行

awk 是解释执行(逐行处理),而非一次性加载所有数据,这使得 awk 非常适合处理大文件

awk 的工作过程分为三个阶段:

step1 BEGIN 块(可选)

  • 只执行一次,在任何输入行被处理之前执行
  • 通常用于初始化变量、设置输出格式、改变分隔符等

step2 主处理块:pattern {action}

  • 对每一行输入数据进行处理
  • 如果指定了 pattern,则仅当该行匹配 pattern 时才执行 action
  • 如果省略 pattern,则对所有行执行 action

step3 END 块(可选)

  • 输入文件全部读完后执行一次
  • 通常用于输出统计结果、清理或总结工作

工作流程图:

START
└── BEGIN 块执行(初始化)
↓
对每行输入:
├── 拆分字段(根据 -F 选项或者 FS 变量)
├── 判断是否匹配 pattern
└── 执行相应的 action
↓
└── END 块执行(收尾总结)
FINISH

1. 逐行读取

  • awk 默认按行读取文件或标准输入,每次处理一整行,处理的行称为记录

2. 字段拆分

  • 每行自动根据 -F fs 选项或者 FS 变量分割成若干字段

3. 模式匹配

  • 正则表达式
  • 条件表达式
  • 范围匹配
  • BEGIN 和 END 模式

4. 动作执行

  • 打印
  • 计算
  • 条件控制
  • 循环控制
选项 含义与作用
-F fs 设置字段分隔符(FS)
-v var=val 向 awk 程序传入变量
-f script.awk 从文件中加载 awk 脚本
-- 明确终止选项解析(用于防止文件名被误认为参数)
--help 显示 awk 使用帮助
--version 显示 awk(如 gawk)版本信息

-F:设置字段分隔符

默认 awk 用空格或 Tab 分隔字段,-F 允许自定义分隔符,可以使用正则表达式:

Terminal window
# 以逗号分割每行,并打印第 1 列和第 2 列
awk -F ',' '{ print $1, $2 }' data.csv
# 将一个或多个连续的英文字母作为分隔符
echo "Hello,World;TEST?FILE$" | awk -F '[a-zA-Z]+' '{print "number of field:" NR,"\n",$1,$2,$3,$4,$5}'

-v:传入变量

Terminal window
awk -F',' -v sum=0 '{ sum += $2 } END { print "Total:", sum }' data.csv

-f:加载外部 awk 脚本

将复杂或多行 awk 脚本写入 .awk 文件,然后通过 -f 执行:

Terminal window
awk -f myscript.awk data.csv

--:终止选项解析

当文件名可能与选项冲突(比如文件名是 -data.txt)时使用:

Terminal window
awk -- '{ print $1 }' -data.txt

如果直接运行 awk '{ print $0 }' -data.txt 会报错,因为 awk 以为用户传了一个选项 -d

变量名 含义说明
$0 当前行的全部内容(未分割前的整行)
$1, $2... 当前行的第 1、第 2 个字段,以此类推
NF 当前行的字段数量(Number of Fields)
NR 当前记录的行号(Number of Record,全局行号,跨多个文件递增)
FNR 当前文件的行号(File Number of Record,每个文件行号从 1 开始)
FS 输入字段分隔符(Field Separator,默认是空格或制表符)
OFS 输出字段分隔符(Output Field Separator,默认是空格)
RS 输入记录分隔符(Record Separator,默认是换行符)
ORS 输出记录分隔符(Output Record Separator,默认是换行符)
FILENAME 当前正在处理的文件名
ARGC 命令行参数的数量
ARGV 命令行参数的数组(ARGV[0] 是脚本名或 awk)

变量中设置分隔符要用双引号,即 "," 而不是 ','

示例数据:

-- data.txt --
Hello,World
Hello,Linux
Hello,awk
-- file1.txt --
This is the first line in file1.txt.
This is the second line in file1.txt.
-- file2.txt --
This is the first line in file2.txt.
This is the second line in file2.txt.

$0:整行内容

Terminal window
awk '{ print $0 }' data.txt

$1, $2, …:各字段

Terminal window
awk '{ print $1, $3 }' data.txt

NF:字段数量

Terminal window
# 每行打印字段数
awk '{ print NF }' data.txt
# 打印每行最后一个字段
awk '{ print $NF }' data.txt

NR vs FNR:行号

Terminal window
awk '{ print NR, FNR, $0 }' file1.txt file2.txt
  • NR:全局行号(跨多个文件递增)
  • FNR:当前文件行号(每个文件从 1 开始)

输出:

1 1 This is the first line in file1.txt.
2 2 This is the second line in file1.txt.
3 1 This is the first line in file2.txt.
4 2 This is the second line in file2.txt.

FS:输入字段分隔符

Terminal window
# 通过 -F 选项设置
awk -F ',' '{ print $1 }' data.txt
# 在 BEGIN 块中通过 FS 变量设置
awk 'BEGIN { FS="," } { print $1 }' data.txt

OFS:输出字段分隔符

Terminal window
awk -F ',' 'BEGIN { OFS=" | " } { print $1, $2 }' data.txt

输出:

Hello | World
Hello | Linux
Hello | awk

RS:输入记录分隔符

Terminal window
# 修改输入记录分隔符为 ","
awk 'BEGIN { RS="," } { print $0 }' data.txt

输出:

Hello
World
Hello
Linux
Hello
awk

ORS:输出记录分隔符

Terminal window
# 修改输出记录分隔符为 "\n---\n"
awk 'BEGIN { RS=","; ORS="\n---\n" } { print $0 }' data.txt

输出:

Hello
---
World
Hello
---
Linux
Hello
---
awk
---

FILENAME:当前处理的文件名

Terminal window
awk '{ print FILENAME, $0 }' file1.txt file2.txt

输出:

file1.txt This is the first line in file1.txt.
file1.txt This is the second line in file1.txt.
file2.txt This is the first line in file2.txt.
file2.txt This is the second line in file2.txt.

示例数据:

-- data.csv --
Alice,80
Bob,90
Charlie,70

1. BEGIN 块中设定变量

Terminal window
awk -F',' 'BEGIN { sum = 0 } { sum += $2 } END { print "Total:", sum }' data.csv

输出:

Total: 240

2. -v 选项传入变量

Terminal window
awk -F',' -v sum=0 '{ sum += $2 } END { print "Total:", sum }' data.csv

输出:

Total: 240
类型 示例 含义
空 { print $1 } 对所有行都执行
正则表达式 /error/ { print $0 } 匹配包含 error 的行
比较表达式 $3 > 100 第三列大于 100 的行
模式组合(逻辑) $2 > 50 && /ok/ 第二列大于 50 且整行包含 ok
范围模式(区间) /BEGIN/,/END/ 从匹配 BEGIN 的行到 END 的行
特殊模式 BEGIN/END BEGIN { ... } END { ... } 在开始前执行一次,结束后执行一次

常见的 awk action 类型:打印输出、变量运算、字符串运算、流程控制、内建函数、系统交互。

print:普通打印

  • 自动添加输出字段分隔符(OFS)
  • 默认使用空格作为分隔符(可以通过 OFS 设置)
Terminal window
awk '{ print $1, $3 }' file.txt
awk 'BEGIN { OFS="," } { print $1, $2 }' file.txt

printf:格式控制打印

  • 更灵活的格式控制(与 C 语言几乎一致)
    • %d:十进制整数
    • %c:单个字符
    • %s:字符串,%-10s 左对齐,占 10 宽度
    • %f:浮点数,%6.2f 表示宽度 6,小数点后保留 2 位
  • 不自动添加 OFS
Terminal window
awk '{ printf "%-10s %6.2f\n", $1, $2 }' file.txt
  • 变量类型是动态的(自动根据上下文转换为数字或字符串)
  • 变量可以在 BEGIN 块中声明并赋值;也可以不声明直接使用,默认初始值为 0(数字)或空串(字符串)
  • 可以在 BEGIN、END 或每一行中使用变量

常见算术运算符

运算类型 示例 说明
赋值 x = 5 基本赋值
加法 x = x + 1 也可以写作 x += 1
自增/自减 x++, --y 和 C 一样,支持前后缀形式
减法 x = x - 2 也支持 x -= 2
乘除 x *= 3, x /= 2 乘法与除法
求余 x % 3 取模
条件 x > y, x == y 比较运算符用于判断
三目运算符 x > 0 ? x : -x 如果 x > 0 就取 x,否则 -x

累加示例:统计总和

Terminal window
awk '{ sum += $1 } END { print "Total:", sum }' data.txt

求平均值

Terminal window
awk '{ sum += $1 } END { print "Average:", sum/NR }' data.txt
  • awk 变量可以存储字符串,不需要声明类型
  • 字符串用双引号 " 括起来
  • 字符串可以直接赋值给变量

1. 字符串拼接

awk 中字符串拼接直接把字符串或变量放在一起即可,不需要用 +:

Terminal window
awk 'BEGIN {
str1 = "Hello"
str2 = "World"
result = str1 str2
print result # 输出 HelloWorld
result2 = str1 " " str2
print result2 # 输出 Hello World
}'

2. 字符串比较

支持 ==、!=、<、<=、>、>=,比较按字典顺序(ASCII)进行:

Terminal window
awk 'BEGIN {
a = "apple"
b = "banana"
if (a < b) print a " < " b
else print a " >= " b
}'

3. 正则表达式匹配字符串

  • ~ 表示匹配
  • !~ 表示不匹配
Terminal window
awk '{
if ($0 ~ /pattern/) print $0
}'
控制结构 作用
if / else 条件判断
while 条件循环
for 计数循环
break / continue 控制循环流程
next 结束当前记录,跳到下一行
exit 立即终止程序

1. if-else

Terminal window
awk '{
if ($2 >= 60)
print $1, "pass";
else
print $1, "fail";
}' scores.txt

2. while

Terminal window
awk 'BEGIN {
i = 1
while (i < 100) {
print i
i *= 2
}
}'

3. for

Terminal window
awk 'NR <= 5 {
for (i = 1; i <= NF; i++)
print "Field", i, "=", $i
}' file.txt

4. next 与 exit

关键字 用途
next 结束当前记录,跳到下一行
exit 立即结束程序执行
Terminal window
# 跳过空行
awk 'NF == 0 { next } { print "Non-empty:", $0 }' file.txt
# 只处理前 3 行
awk 'NR > 3 { exit } { print }' file.txt

1. 字符串函数

函数名 作用 示例
length(s) 返回字符串 s 的长度,s 可省略表示当前记录($0) length("hello") -> 5
substr(s, m, n) 从字符串 s 的第 m 个字符开始,取 n 个字符 substr("abcdef", 2, 3) -> "bcd"
index(s, t) 返回子串 t 在 s 中的位置,找不到返回 0 index("hello", "ll") -> 3
split(s, a, sep) 把字符串 s 按分隔符 sep 拆分到数组 a 中 split("a,b,c", arr, ",") 后 arr[1]=“a”
tolower(s) 返回字符串 s 的小写版本(GNU awk) tolower("Hello") -> "hello"
toupper(s) 返回字符串 s 的大写版本(GNU awk) toupper("Hello") -> "HELLO"
gsub(r, t, s) 全局替换字符串 s 中所有匹配正则 r 的部分为 t gsub("foo", "bar", s)
sub(r, t, s) 替换字符串 s 中第一个匹配正则 r 的部分为 t sub("foo", "bar", s)

2. 数学函数

函数名 作用 示例
sin(x) 返回 x 的正弦(弧度) sin(3.14/2)
cos(x) 返回 x 的余弦(弧度) cos(0)
atan2(y,x) 返回 y/x 的反正切(考虑象限) atan2(1,1)
exp(x) 返回 e 的 x 次幂 exp(1)
log(x) 返回自然对数 log(10)
sqrt(x) 返回平方根 sqrt(9)
int(x) 返回 x 的整数部分(向零取整) int(3.7) -> 3
rand() 返回 0 到 1 之间的伪随机数 rand()
srand(x) 设置随机数种子 srand(1234)

3. 时间函数

函数名 作用 示例
systime() 返回当前时间的 Unix 时间戳(秒) print systime()
strftime(fmt, timestamp) 格式化时间,fmt 是格式字符串,timestamp 默认为当前时间 strftime("%Y-%m-%d %H:%M:%S")

常用时间格式说明:

格式符 含义 例子
%Y 4 位年份 2025
%m 月份(01-12) 08
%d 日期(01-31) 02
%H 小时(00-23) 15
%M 分钟(00-59) 45
%S 秒(00-59) 30

1. 执行外部命令

使用 system() 执行外部命令:

  • system(command) 会调用 shell 执行 command 字符串里的命令
  • 返回值是命令的退出状态码(0 通常表示成功)
  • 适合执行单条命令,不返回命令输出
Terminal window
awk 'BEGIN {
ret = system("date")
print "Exit code:", ret
}'

2. 读写文件

  • getline < "filename" 从文件读取一行
  • print ... > "filename" 向文件写入(覆盖)
  • print ... >> "filename" 向文件追加
Terminal window
# 读取文件第一行并打印
awk 'BEGIN {
getline line < "data.txt"
print "First line is:", line
}'
# 写入新内容到文件(覆盖)
awk 'BEGIN {
print "Hello, World!" > "output.txt"
}'
# 向文件追加内容
awk 'BEGIN {
print "First line" > "output.txt"
print "Second line" >> "output.txt"
print "Third line" >> "output.txt"
}'