awk
awk 是一种用于文本和数据处理的编程语言。数据可以来自标准输入、文件或其他命令的输出。awk 提供变量、数学运算、字符串处理等编程语言功能,特别适用于按列(字段)分析和处理结构化文本(如 CSV、日志文件、配置文件等)
awk [options] 'pattern {action}' fileoptions:用于控制 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 块执行(收尾总结)FINISH1. 逐行读取
- 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 允许自定义分隔符,可以使用正则表达式:
# 以逗号分割每行,并打印第 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:传入变量
awk -F',' -v sum=0 '{ sum += $2 } END { print "Total:", sum }' data.csv-f:加载外部 awk 脚本
将复杂或多行 awk 脚本写入 .awk 文件,然后通过 -f 执行:
awk -f myscript.awk data.csv--:终止选项解析
当文件名可能与选项冲突(比如文件名是 -data.txt)时使用:
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,WorldHello,LinuxHello,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:整行内容
awk '{ print $0 }' data.txt$1, $2, …:各字段
awk '{ print $1, $3 }' data.txtNF:字段数量
# 每行打印字段数awk '{ print NF }' data.txt# 打印每行最后一个字段awk '{ print $NF }' data.txtNR vs FNR:行号
awk '{ print NR, FNR, $0 }' file1.txt file2.txtNR:全局行号(跨多个文件递增)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:输入字段分隔符
# 通过 -F 选项设置awk -F ',' '{ print $1 }' data.txt
# 在 BEGIN 块中通过 FS 变量设置awk 'BEGIN { FS="," } { print $1 }' data.txtOFS:输出字段分隔符
awk -F ',' 'BEGIN { OFS=" | " } { print $1, $2 }' data.txt输出:
Hello | WorldHello | LinuxHello | awkRS:输入记录分隔符
# 修改输入记录分隔符为 ","awk 'BEGIN { RS="," } { print $0 }' data.txt输出:
HelloWorldHelloLinuxHelloawkORS:输出记录分隔符
# 修改输出记录分隔符为 "\n---\n"awk 'BEGIN { RS=","; ORS="\n---\n" } { print $0 }' data.txt输出:
Hello---WorldHello---LinuxHello---awk
---FILENAME:当前处理的文件名
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,80Bob,90Charlie,701. BEGIN 块中设定变量
awk -F',' 'BEGIN { sum = 0 } { sum += $2 } END { print "Total:", sum }' data.csv输出:
Total: 2402. -v 选项传入变量
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设置)
awk '{ print $1, $3 }' file.txtawk 'BEGIN { OFS="," } { print $1, $2 }' file.txtprintf:格式控制打印
- 更灵活的格式控制(与 C 语言几乎一致)
%d:十进制整数%c:单个字符%s:字符串,%-10s左对齐,占 10 宽度%f:浮点数,%6.2f表示宽度 6,小数点后保留 2 位
- 不自动添加
OFS
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 |
累加示例:统计总和
awk '{ sum += $1 } END { print "Total:", sum }' data.txt求平均值
awk '{ sum += $1 } END { print "Average:", sum/NR }' data.txt- awk 变量可以存储字符串,不需要声明类型
- 字符串用双引号
"括起来 - 字符串可以直接赋值给变量
1. 字符串拼接
awk 中字符串拼接直接把字符串或变量放在一起即可,不需要用 +:
awk 'BEGIN { str1 = "Hello" str2 = "World" result = str1 str2 print result # 输出 HelloWorld
result2 = str1 " " str2 print result2 # 输出 Hello World}'2. 字符串比较
支持 ==、!=、<、<=、>、>=,比较按字典顺序(ASCII)进行:
awk 'BEGIN { a = "apple" b = "banana" if (a < b) print a " < " b else print a " >= " b}'3. 正则表达式匹配字符串
~表示匹配!~表示不匹配
awk '{ if ($0 ~ /pattern/) print $0}'| 控制结构 | 作用 |
|---|---|
if / else |
条件判断 |
while |
条件循环 |
for |
计数循环 |
break / continue |
控制循环流程 |
next |
结束当前记录,跳到下一行 |
exit |
立即终止程序 |
1. if-else
awk '{ if ($2 >= 60) print $1, "pass"; else print $1, "fail";}' scores.txt2. while
awk 'BEGIN { i = 1 while (i < 100) { print i i *= 2 }}'3. for
awk 'NR <= 5 { for (i = 1; i <= NF; i++) print "Field", i, "=", $i}' file.txt4. next 与 exit
| 关键字 | 用途 |
|---|---|
next |
结束当前记录,跳到下一行 |
exit |
立即结束程序执行 |
# 跳过空行awk 'NF == 0 { next } { print "Non-empty:", $0 }' file.txt
# 只处理前 3 行awk 'NR > 3 { exit } { print }' file.txt1. 字符串函数
| 函数名 | 作用 | 示例 |
|---|---|---|
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 通常表示成功)
- 适合执行单条命令,不返回命令输出
awk 'BEGIN { ret = system("date") print "Exit code:", ret}'2. 读写文件
getline < "filename"从文件读取一行print ... > "filename"向文件写入(覆盖)print ... >> "filename"向文件追加
# 读取文件第一行并打印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"}'man awk- GNU Awk User’s Guide