awk 命令使用详细总结
awk 命令使用详细总结
awk 是 Unix/Linux 生态中模式驱动的文本处理编程语言,由 Alfred Aho、Peter Weinberger、Brian Kernighan 三位开发者创造,是 Shell 三剑客(grep/sed/awk)中功能最全面的工具,核心用于文本扫描、字段提取、数据统计、格式化输出,原生支持变量、数组、循环、函数等完整编程能力,是运维、开发人员的核心文本处理工具。
Linux 系统中默认的 awk 是 GNU awk(gawk),是标准 awk 的超集,提供了大量扩展功能,本文默认基于 gawk 讲解,同时标注 POSIX 标准兼容内容。
一、核心语法与执行流程
1. 基础命令格式
# 单行命令模式(最常用)
awk [选项] '模式{动作}' 输入文件1 输入文件2...
# 脚本文件模式(适合复杂逻辑)
awk [选项] -f 脚本文件.awk 输入文件-
模式(Pattern):筛选条件,只有匹配模式的行才会执行对应的动作,省略则匹配所有行
-
动作(Action):对匹配行执行的代码,用
{}包裹,省略则默认执行{print $0}(打印整行) -
输入文件省略时,默认从标准输入(stdin)读取数据
2. 脚本三大核心块
一个完整的 awk 脚本由三个可选块组成,分工明确:
awk '
BEGIN { 预处理动作 } # 1. 读取输入前执行,仅执行1次
模式{ 逐行处理动作 } # 2. 逐行读取输入,匹配模式则执行,循环执行
END { 收尾汇总动作 } # 3. 所有输入读取完成后执行,仅执行1次
' 输入文件-
BEGIN 块:常用于初始化变量、设置分隔符、打印表头、加载配置
-
主体块:核心处理逻辑,逐行扫描文本的核心循环
-
END 块:常用于统计结果输出、数据汇总、格式化报表
3. 完整执行流程
-
解析命令行选项与参数
-
执行 BEGIN 块中的所有代码
-
循环执行:读取一行输入 → 拆分字段 → 匹配模式 → 执行对应动作,直到输入读取完毕
-
执行 END 块中的所有代码
-
退出程序,返回退出码
4. 高频命令行选项
| 选项 | 功能说明 | 典型示例 |
|---|---|---|
-F 分隔符 |
指定输入字段分隔符,支持字符串/正则,等价于内置变量 FS |
awk -F: '{print $1}' /etc/passwd(冒号分隔) |
-v 变量名=值 |
执行脚本前定义/传递变量,支持外部 Shell 变量传入 | awk -v num=10 'BEGIN{print num+5}' |
-f 脚本文件 |
从指定文件读取 awk 脚本,适合复杂逻辑封装 | awk -f handle.awk data.txt |
-W posix |
严格遵循 POSIX 标准,禁用 gawk 扩展特性 | awk -W posix '{print $1}' file |
-W compat |
兼容传统 awk 语法,禁用 gawk 扩展 | awk -W compat '{print $1}' file |
二、核心内置变量
awk 提供了丰富的内置变量,无需声明即可直接使用,按用途分为以下几类:
1. 记录(行)相关变量
| 变量 | 功能说明 | 核心用法 |
|---|---|---|
$0 |
当前读取的整行完整内容 | awk '{print $0}' file 打印整行 |
NR |
Number of Record,已读取的总记录数(全局行号),多文件连续计数 | awk 'NR==5' file 打印第5行 |
FNR |
File Number of Record,当前文件的记录数(文件内行号),多文件单独计数 | awk 'FNR==1{print FILENAME}' file1 file2 打印每个文件名 |
RS |
Record Separator,输入记录分隔符,**默认值为换行符 ** \n |
awk 'BEGIN{RS=""} {print $1}' file 按空行分隔段落 |
ORS |
Output Record Separator,输出记录分隔符,**默认值为换行符 ** \n |
awk 'BEGIN{ORS=" "} {print $0}' file 将所有行合并为一行 |
2. 字段(列)相关变量
| 变量 | 功能说明 | 核心用法 |
|---|---|---|
$1~$n |
当前行的第 n 个字段,由 FS 拆分 |
awk '{print $1, $3}' file 打印第1、3列 |
NF |
Number of Fields,当前行的字段总数(列数) | awk '{print $NF}' file 打印最后一列;$(NF-1) 打印倒数第二列 |
FS |
Field Separator,输入字段分隔符,默认值为连续空白符(空格/制表符/换行符) | awk 'BEGIN{FS=":"} {print $1}' /etc/passwd |
OFS |
Output Field Separator,输出字段分隔符,默认值为单个空格 | awk 'BEGIN{OFS=","} {print $1,$3}' file 输出用逗号分隔 |
3. 文件与参数相关变量
| 变量 | 功能说明 | 核心用法 |
|---|---|---|
FILENAME |
当前正在处理的输入文件名 | awk '{print FILENAME, FNR, $0}' file1 file2 |
ARGC |
命令行参数的总个数 | awk 'END{print ARGC}' file1 file2 |
ARGV |
命令行参数数组,ARGV[0] 固定为 awk,ARGV[1] 开始为输入文件/参数 |
awk 'BEGIN{print ARGV[1]}' file1 |
ENVIRON |
系统环境变量的关联数组 | awk 'BEGIN{print ENVIRON["HOME"]}' 打印用户家目录 |
4. 正则与格式相关变量
| 变量 | 功能说明 |
|---|---|
RSTART |
match() 函数匹配到的字符串的起始位置,匹配失败为0 |
RLENGTH |
match() 函数匹配到的字符串的长度,匹配失败为-1 |
SUBSEP |
多维数组下标的分隔符,默认值为不可打印字符 \034 |
OFMT |
数字的默认输出格式,默认值为 %.6g |
CONVFMT |
数字转字符串的默认格式,默认值为 %.6g |
IGNORECASE |
gawk 扩展,设置为1时,正则匹配忽略大小写,默认0 |
FPAT |
gawk 扩展,定义字段的正则匹配规则,替代分隔符模式,适合处理 CSV 等复杂格式 |
三、运算符与模式匹配
1. 完整运算符体系(优先级从高到低)
| 运算符类型 | 运算符列表 | 说明 |
|---|---|---|
| 字段引用 | $ |
字段引用,如 $1、$NF |
| 自增自减 | ++ -- |
前缀/后缀自增、自减,同C语言 |
| 幂运算 | ^ ** |
幂运算,2^3=8,** 为 gawk 扩展 |
| 算术运算 | * / % |
乘、除、取模 |
+ - |
加、减 | |
| 字符串拼接 | 空格 | 隐式拼接,"a" "b" 等价于 "ab" |
| 关系运算 | > < >= <= |
数值/字符串比较 |
== != |
精确等于、不等于 | |
~ !~ |
正则匹配、正则不匹配(核心) | |
| 逻辑运算 | ! |
逻辑非 |
&& |
逻辑与(短路) | |
| ` | ||
| 三元运算 | ?: |
条件?表达式1:表达式2,同C语言 |
| 赋值运算 | = += -= *= /= %= ^= |
赋值与复合赋值 |
| 数组成员 | in |
判断下标是否存在于数组中 |
2. 核心模式类型
awk 是模式驱动的语言,模式决定了动作是否执行,支持以下6种模式:
(1)空模式
省略模式,匹配所有输入行,每行都会执行动作,是最基础的用法:
awk '{print $1}' file # 所有行都打印第1列(2)正则表达式模式
格式:/正则表达式/,匹配内容符合正则的行,支持 POSIX 正则规范:
awk '/error/' file # 打印包含 error 的行
awk '$1 ~ /^root/' /etc/passwd # 第1列以 root 开头的行
awk '$7 !~ /nologin$/' /etc/passwd # 第7列不以 nologin 结尾的行(3)关系表达式模式
用关系运算符构成的条件,条件为真时执行动作:
awk 'NR>5 && NR<10' file # 打印第6-9行
awk -F: '$3>=1000' /etc/passwd # 打印普通用户(UID>=1000)
awk 'NF==5' file # 打印字段数为5的行(4)范围模式
格式:模式1,模式2,从匹配模式1的行开始,到匹配模式2的行结束,中间所有行都执行动作:
awk '/start/,/end/' file # 打印 start 到 end 之间的所有行
awk 'NR==10,NR==20' file # 打印第10到20行注意:范围模式是贪婪匹配,若没有匹配到模式2,会一直匹配到文件末尾。
(5)特殊模式
-
BEGIN:读取输入前执行,仅执行1次 -
END:所有输入读取完成后执行,仅执行1次 -
BEGINFILE/ENDFILE:gawk 扩展,处理每个文件的前后分别执行,适合多文件处理的初始化/收尾
(6)模式组合
支持用逻辑运算符 && || ! 组合多个模式,实现复杂筛选:
awk 'NR>10 && /error/ && $NF>500' file # 10行后、包含error、最后一列大于500的行四、流程控制语句
awk 支持类C语言的流程控制语法,可实现复杂的逻辑处理。
1. 条件判断
# 单分支
if (条件) {
动作
}
# 双分支
if (条件) {
动作1
} else {
动作2
}
# 多分支
if (条件1) {
动作1
} else if (条件2) {
动作2
} else {
动作3
}示例:
awk -F: '{
if($3==0) print "管理员用户:"$1;
else if($3>=1000) print "普通用户:"$1;
else print "系统用户:"$1;
}' /etc/passwd2. 循环语句
# for 循环(固定次数)
for (初始化; 条件; 递增) {
动作
}
# for 循环(遍历数组)
for (下标 in 数组) {
动作
}
# while 循环
while (条件) {
动作
}
# do-while 循环(先执行一次,再判断)
do {
动作
} while (条件)示例1:遍历字段
awk '{for(i=1;i<=NF;i++) print "第"i"列:"$i}' file示例2:遍历数组
awk '{arr[$1]++} END{for(i in arr) print i, arr[i]}' access.log3. 流程控制关键字
| 关键字 | 功能说明 |
|---|---|
break |
立即跳出当前循环,执行循环后的代码 |
continue |
跳过本次循环剩余代码,直接进入下一次循环 |
next |
跳过当前行的所有后续处理,直接读取下一行,进入下一轮主循环 |
exit n |
立即终止主循环,执行 END 块,n 为退出码(省略则为0) |
| 示例: |
awk '/^#/{next} {print $0}' file # 跳过注释行,打印其他行
awk 'NR>100{exit} {print NR, $0}' file # 打印前100行后退出五、内置函数大全
awk 提供了丰富的内置函数,覆盖字符串、数值、时间、IO 等场景,是文本处理的核心能力。
1. 高频字符串处理函数
| 函数 | 语法 | 功能说明 |
|---|---|---|
length |
length(str) |
返回字符串 str 的长度,无参数则返回 $0 的长度 |
index |
index(str, substr) |
返回 substr 在 str 中首次出现的位置,找不到返回0 |
substr |
substr(str, start, len) |
从 str 的 start 位置截取 len 个字符,len 省略则截取到末尾 |
split |
split(str, arr, sep) |
将 str 按 sep 拆分存入数组 arr,返回字段总数,sep 省略则使用 FS |
match |
match(str, regex) |
在 str 中匹配正则 regex,返回匹配起始位置,设置 RSTART 和 RLENGTH,找不到返回0 |
sub |
sub(regex, repl, str) |
替换 str 中第一个匹配 regex 的内容为 repl,str 省略则为 $0,支持 & 引用匹配内容 |
gsub |
sub(regex, repl, str) |
全局替换,替换 str 中所有匹配 regex 的内容,其他同 sub |
tolower |
tolower(str) |
将字符串 str 全部转为小写 |
toupper |
toupper(str) |
将字符串 str 全部转为大写 |
sprintf |
sprintf(format, args) |
格式化字符串,返回格式化后的结果,用法同C语言 sprintf |
| 示例: |
# 全局替换
awk '{gsub(/foo/, "bar"); print}' file
# 截取字符串
awk '{print substr($0, 1, 10)}' file # 截取每行前10个字符
# 字符串拆分
awk '{split($0, arr, ","); print arr[1], arr[3]}' file.csv2. 数值计算函数
| 函数 | 功能说明 |
|---|---|
int(x) |
对 x 取整,向0截断,如 int(3.9)=3,int(-3.9)=-3 |
sqrt(x) |
返回 x 的平方根 |
exp(x) |
返回 e 的 x 次方 |
log(x) |
返回 x 的自然对数 |
sin(x)/cos(x)/atan2(y,x) |
三角函数 |
rand() |
返回 0~1 之间的随机浮点数 |
srand(x) |
设置随机数种子,x 省略则使用当前系统时间,返回旧种子 |
| 示例: |
# 生成0-100的随机整数
awk 'BEGIN{srand(); print int(rand()*100)}'3. 时间处理函数(gawk 扩展)
| 函数 | 语法 | 功能说明 |
|---|---|---|
systime() |
systime() |
返回当前时间的 Unix 时间戳(秒级) |
strftime |
strftime(format, timestamp) |
将时间戳格式化为指定格式的字符串,format 同 date 命令 |
mktime |
mktime("YYYY MM DD HH MM SS") |
将时间字符串转为 Unix 时间戳 |
| 示例: |
# 打印当前时间
awk 'BEGIN{print strftime("%Y-%m-%d %H:%M:%S", systime())}'4. IO 与系统函数
| 函数 | 功能说明 |
|---|---|
getline |
读取下一行输入,可赋值给变量,成功返回1,EOF返回0,出错返回-1 |
close(filename) |
关闭打开的文件/管道,避免 too many open files 错误 |
system(cmd) |
执行 Shell 命令,返回命令的退出码 |
fflush() |
刷新输出缓冲区,实时打印内容 |
| 示例: |
# 执行Shell命令
awk 'BEGIN{system("ls -l")}'
# 读取下一行
awk '{getline next_line; print $0, next_line}' file六、数组(关联数组)
awk 的数组是关联数组(哈希表),是其核心特色之一,下标可以是数字、字符串,无需提前声明大小,支持动态扩容。
1. 数组定义与赋值
# 直接赋值
arr["name"] = "zhangsan"
arr[1] = 100
arr["ip_192.168.1.1"] = 5
# 批量赋值(拆分字符串)
split("a,b,c,d", arr, ",") # arr[1]=a, arr[2]=b, arr[3]=c, arr[4]=d2. 数组遍历
awk 数组遍历只能用 for (下标 in 数组) 的格式,遍历的是数组的下标,而非值,且遍历顺序不固定:
awk '{arr[$1]++} END{
for (key in arr) {
print "key:", key, "value:", arr[key]
}
}' access.log3. 成员判断与删除
-
成员判断:必须用
if (key in arr),避免访问不存在的下标时自动创建元素 -
删除元素:用
delete关键字
# 判断成员是否存在
if ("root" in user_arr) {
print "root 存在"
}
# 删除单个元素
delete arr["root"]
# 删除整个数组
delete arr4. 多维数组模拟
awk 不支持真正的多维数组,通过 SUBSEP 分隔符模拟多维数组:
# 二维数组赋值
arr["a", "b"] = 100
# 等价于 arr["a" SUBSEP "b"] = 100
# 二维数组遍历
for (key in arr) {
split(key, idx, SUBSEP)
print "第一维:"idx[1], "第二维:"idx[2], "值:"arr[key]
}七、高频实战场景(可直接复制使用)
1. 基础列提取与格式化
# 提取/etc/passwd的用户名和登录Shell
awk -F: '{print $1, $7}' /etc/passwd
# 格式化输出,左对齐用户名,右对齐UID
awk -F: '{printf "%-20s %5d\n", $1, $3}' /etc/passwd
# 打印最后一列
awk '{print $NF}' file
# 打印除最后一列外的所有列
awk '{$NF=""; print $0}' file2. 行过滤与筛选
# 打印第5行
awk 'NR==5' file
# 打印前10行(替代head)
awk 'NR<=10' file
# 打印包含error的行(替代grep)
awk '/error/' file
# 打印不包含comment的行(替代grep -v)
awk '!/comment/' file
# 打印空行的行号
awk '/^$/{print NR}' file
# 打印行号大于5且字段数等于6的行
awk 'NR>5 && NF==6' file3. 数值统计与计算
# 统计文件行数(替代wc -l)
awk 'END{print NR}' file
# 统计第二列的总和
awk '{sum+=$2} END{print "总和:", sum}' file
# 统计第三列的平均值
awk '{sum+=$3; n++} END{print "平均值:", sum/n}' file
# 统计最大值、最小值
awk '
BEGIN{max=0; min=999999}
{
if($2>max) max=$2;
if($2<min) min=$2;
}
END{print "最大值:", max, "最小值:", min}
' file4. 文本去重与频次统计
# 整行去重,保留第一次出现的行(替代uniq,无需排序)
awk '!arr[$0]++' file
# 统计第一列的不重复值出现次数
awk '{arr[$1]++} END{for(i in arr) print i, arr[i]}' file
# 统计Nginx访问日志中IP的访问次数,按次数降序排序
awk '{arr[$1]++} END{for(i in arr) print i, arr[i]}' access.log | sort -rnk2
# 统计HTTP状态码的出现次数
awk '{arr[$9]++} END{for(k in arr) print "状态码:"k, "次数:"arr[k]}' access.log5. 文本替换与修改
# 将所有foo替换为bar(替代sed)
awk '{gsub(/foo/, "bar"); print}' file
# 仅在包含error的行中替换foo为bar
awk '/error/{gsub(/foo/, "bar")} {print}' file
# 去掉行首空格
awk '{sub(/^[ \t]+/, ""); print}' file
# 去掉行尾空格
awk '{sub(/[ \t]+$/, ""); print}' file
# 去掉行首行尾空格
awk '{gsub(/^[ \t]+|[ \t]+$/, ""); print}' file6. 日志分析实战
# 找出Nginx日志中响应时间超过500ms的慢请求
awk '{
gsub(/cost=|ms/, "", $4);
if($4 > 500) print "慢请求:" $0
}' access.log
# 打印匹配error的行及其前后2行
awk '{a[NR]=$0} /error/{for(i=NR-2;i<=NR+2;i++) if(i>0) print a[i]}' file
# 统计SSH登录失败的IP地址
awk '/Invalid user/{arr[$10]++} END{for(i in arr) print i, arr[i]}' /var/log/secure7. 多文件处理
# 分别统计每个文件的行数
awk '
FNR==1{
if(NR>1) print fname, cnt;
fname=FILENAME;
cnt=0;
}
{cnt++}
END{print fname, cnt}
' file1 file2 file3
# 合并两个文件,按关键字匹配
awk -F: 'NR==FNR{a[$1]=$3; next} {print $1, a[$1]}' file1 file2八、高级用法
1. 自定义函数
awk 支持自定义函数,实现代码复用,语法如下:
function 函数名(参数列表) {
函数体
return 返回值 # 可选
}示例:
awk '
# 定义求最大值的函数
function max(a, b) {
return a > b ? a : b
}
# 主逻辑
{
print max($1, $2)
}
' file2. 复杂格式处理(CSV)
gawk 提供 FPAT 变量,可定义字段的正则规则,完美处理带引号、包含分隔符的 CSV 文件:
# 处理标准CSV,正确解析带引号的字段
awk '
BEGIN{
FPAT = "([^,]+)|(\"[^\"]+\")" # 定义CSV字段规则
OFS = ","
}
{
print $1, $3, $5 # 提取第1、3、5列
}
' data.csv3. 与Shell交互
(1)向awk传递Shell变量
# 方法1:-v 选项(推荐,支持特殊字符)
name="root"
awk -v user="$name" -F: '$1==user{print $0}' /etc/passwd
# 方法2:环境变量
export name="root"
awk -F: '$1==ENVIRON["name"]{print $0}' /etc/passwd(2)awk中调用Shell命令
# 用system()执行命令
awk 'BEGIN{system("echo 当前时间:$(date +%Y-%m-%d)")}'
# 用管道获取命令输出
awk 'BEGIN{
while("ls -l" | getline line) {
print line
}
close("ls -l")
}'4. 大文件处理优化
-
流式处理:避免用数组缓存所有行,尽量逐行处理,减少内存占用
-
提前终止:满足条件时用
exit提前退出,避免读取整个文件 -
减少正则:能用字符串精确匹配(
==)就不用正则匹配(~) -
及时关闭文件/管道:用
close()关闭打开的文件,避免句柄泄漏 -
前置筛选:用
grep先过滤核心行,再用 awk 处理,减少 awk 处理的数据量
九、避坑指南与注意事项
-
字段分隔符的坑
-
默认
FS是连续空白符,多个空格/制表符会合并为一个分隔符,行首行尾的空白会被忽略;而-F" "是单个空格作为分隔符,二者行为不同 -
特殊字符分隔符(如
|*.)是正则元字符,需要转义,如-F"\|"或-F"[|]" -
多分隔符用正则字符集,如
-F"[:,;]"同时用冒号、逗号、分号分隔
-
-
变量与类型的坑
-
awk 是弱类型语言,字符串和数字自动转换,非数字字符串参与运算时会被视为0,如
"abc"+123=123 -
字符串精确匹配必须用双引号包裹,如
$1=="root",$1==root会把root视为变量,而非字符串 -
浮点数运算存在精度问题,如
0.1+0.2!=0.3,处理金额时建议放大为整数运算
-
-
数组使用的坑
-
访问不存在的数组下标时,会自动创建该下标,值为空字符串,因此判断成员是否存在必须用
if (key in arr),而非if (arr[key]!="") -
for (i in arr)遍历的是数组的下标,而非值,且遍历顺序不固定,需要有序输出需自行排序
-
-
正则匹配的坑
-
~是包含匹配,==是精确匹配,二者不可混用。如$1 ~ /root/匹配包含 root 的内容,$1 == "root"仅匹配完全等于 root 的内容 -
正则匹配默认区分大小写,忽略大小写需设置
IGNORECASE=1(gawk 扩展) -
范围模式
/start/,/end/是贪婪匹配,没有结束标记会匹配到文件末尾
-
-
跨平台兼容性
-
IGNORECASE、FPAT、BEGINFILE、systime()等是 gawk 扩展,POSIX 标准 awk 不支持,跨平台脚本需避免使用 -
不同系统的 awk 版本差异较大,Linux 用 gawk,macOS 默认用 BSD awk,很多扩展功能不支持
-
-
其他常见坑
-
空行的
NF=0,此时$NF等价于$0,也就是空行,使用前需判断NF>0 -
awk 脚本放在单引号中,单引号内无法直接使用单引号,需用
'''或\x27替代 -
next仅跳过当前行的主循环处理,不会跳过 END 块;exit会终止主循环,但仍会执行 END 块
-
(注:文档部分内容可能由 AI 生成)