1. 前言:进阶的意义
通过前两篇的学习,你已经掌握了awk的基础运行方式和核心命令行参数,能够处理大部分简单的文本处理需求(比如筛选行、提取字段、格式化输出)。但实际工作中,我们遇到的文本处理场景往往更复杂——比如统计行数、计算字段总和、按条件循环处理、加载外部脚本库,这时候就需要用到awk的高级功能。
本篇重点讲解:awk内置变量、内置函数、流程控制、环境变量、文件包含与动态扩展,每个知识点都配详尽示例,确保你学完就能应对复杂场景,真正做到“精通awk”。
提示:所有示例依然基于前两篇的test.txt和test.csv文件,若你没有,可参考前两篇的命令重新创建。
2. 核心进阶:awk内置变量(必掌握)
awk有很多内置变量(系统自带,无需手动定义),这些变量能帮我们快速获取文本的关键信息(比如行号、字段数、文件名),极大提升处理效率。重点讲解10个最常用的内置变量,按用途分类,方便记忆。
2.1 行与字段相关变量(最常用)
| 内置变量 | 说明 | 实操示例 |
|---|---|---|
| NR | Number of Records,当前处理的行号(所有文件累计) | awk '{print "行号:"NR, "内容:"$0}' test.txt(打印每行行号和内容) |
| FNR | File Number of Records,当前文件的行号(多文件处理时,每个文件重新计数) | awk '{print "当前文件行号:"FNR, "内容:"$0}' test.txt test.csv(处理两个文件,分别计数) |
| NF | Number of Fields,当前行的字段数($1到$NF就是当前行的所有字段) | awk -F "," '{print "当前行字段数:"NF, "最后一列:"$NF}' test.csv(打印每行列数和最后一列) |
| FS | Field Separator,字段分隔符(和-F参数功能一致,可在脚本中动态修改) | awk 'BEGIN {FS=","} {print $1, $2}' test.csv(在BEGIN中设置分隔符,等价于-F ",") |
| OFS | Output Field Separator,输出字段分隔符(默认是空格,可修改) | awk -F "," 'BEGIN {OFS="|"} {print $1, $2}' test.csv(输出时用|分隔字段) |
实操示例(重点练习,可直接复制):
# 示例1:用NR和NF筛选行(筛选字段数大于3的行,且行号大于1)
awk -F "," 'NR>1 && NF>3 {print "行号"NR, "字段数"NF, "内容:"$0}' test.csv
# 输出(test.csv中,数据行字段数都是4,表头字段数4,跳过表头):
# 行号2 字段数4 内容:张三,20,男,北京
# 行号3 字段数4 内容:李四,25,女,上海
# 行号4 字段数4 内容:王五,30,男,广州
# 示例2:用FNR处理多个文件(分别显示两个文件的行号)
awk '{print "文件:"FILENAME, "行号:"FNR, "内容:"$0}' test.txt test.csv
# 输出(test.txt行号1-3,test.csv行号1-4):
# 文件:test.txt 行号:1 内容:张三 20 男
# ...(省略中间内容)
# 文件:test.csv 行号:1 内容:姓名,年龄,性别,城市
# ...(省略中间内容)
# 示例3:修改OFS,格式化输出(用逗号分隔输出字段)
awk -F "," 'BEGIN {OFS=","} NR>1 {print $1, $2, $4}' test.csv
# 输出(和CSV格式一致):
# 张三,20,北京
# 李四,25,上海
# 王五,30,广州
# 示例4:动态修改FS(先按空格分隔,再按逗号分隔)
echo -e "张三 20,男\n李四 25,女" | awk 'BEGIN {FS=" "} {print $1, $2; FS=","; print $1, $2}'
# 输出(第一行按空格分隔,第二行按逗号分隔):
# 张三 20,男
# 20 男
2.2 文件与参数相关变量
| 内置变量 | 说明 | 实操示例 |
|---|---|---|
| FILENAME | 当前处理的文件名(多文件处理时,显示当前正在处理的文件) | awk '{print "正在处理:"FILENAME, "行号:"NR}' test.txt test.csv |
| ARGC | Argument Count,命令行参数的个数(包括awk命令本身) | awk '{print ARGC}' test.txt test.csv(输出3,因为参数是awk、test.txt、test.csv) |
| ARGV | Argument Vector,命令行参数数组(ARGV[0]是awk,ARGV[1]是第一个文件,以此类推) | awk 'BEGIN {for(i=0;i<ARGC;i++) print ARGV[i]}' test.txt test.csv |
| ARGIND | 当前处理的参数在ARGV数组中的索引(对应ARGV的下标) | awk '{print "当前参数索引:"ARGIND, "文件名:"FILENAME}' test.txt test.csv |
实操示例(重点练习):
# 示例1:用ARGC和ARGV,动态指定处理的文件
# 脚本逻辑:如果参数个数大于2,就处理所有文件,否则提示“缺少文件”
awk 'BEGIN {
if(ARGC<3) {
print "错误:缺少处理文件!"
exit 1 # 退出,状态码1(表示错误)
} else {
print "共处理"ARGC-1"个文件:"
for(i=1;i<ARGC;i++) print ARGV[i]
}
}' test.txt test.csv
# 输出:
# 共处理2个文件:
# test.txt
# test.csv
# 示例2:用ARGIND区分不同文件,分别处理
awk '{
if(ARGIND==1) {print "处理test.txt:"$0} # ARGIND=1对应第一个文件test.txt
if(ARGIND==2) {print "处理test.csv:"$0} # ARGIND=2对应第二个文件test.csv
}' test.txt test.csv
3. 核心进阶:awk内置函数(实用高频)
awk提供了很多内置函数,涵盖字符串处理、数值计算、时间处理等,重点讲解日常工作中最常用的8个,按用途分类,每个函数配详细示例,避免冗余。
3.1 字符串处理函数(最常用)
| 函数名 | 说明 | 语法 | 实操示例 |
|---|---|---|---|
| length() | 计算字符串长度(中文UTF-8按多字节计算,-b参数下按单字节) | length(字符串) | awk '{print $1, "长度:"length($1)}' test.txt(输出张三 长度:2) |
| substr() | 截取字符串(从指定位置开始,截取指定长度) | substr(字符串, 起始位置, 长度) | awk '{print substr($1,1,1)}' test.txt(截取姓名第一个字:张、李、王) |
| index() | 查找字符串在另一个字符串中的位置(找不到返回0) | index(原字符串, 目标字符串) | awk -F "," '{print index($4,"京")}' test.csv(查找“京”在城市中的位置) |
| gsub() | 全局替换字符串(替换所有匹配的内容,区别于sub()只替换第一次) | gsub(匹配规则, 替换后内容, 原字符串) | awk '{gsub("男","男性"); print}' test.txt(将所有“男”替换为“男性”) |
实操示例(重点练习):
# 示例1:用substr()截取日志中的IP(模拟日志)
echo -e "192.168.1.1 - [2024-01-01] \"GET /index.html\"" > access.log
# 截取IP(前11个字符)
awk '{print "访问IP:"substr($1,1,11)}' access.log
# 输出:访问IP:192.168.1.1
# 示例2:用gsub()替换CSV中的逗号为空格,并重命名字段
awk -F "," 'BEGIN {OFS=" "} {gsub(","," ",$0); print $1, $2, $3, $4}' test.csv
# 输出(逗号替换为空格):
# 姓名 年龄 性别 城市
# 张三 20 男 北京
# 示例3:结合index()筛选包含指定字符的行
# 筛选城市中包含“京”的用户
awk -F "," 'NR>1 && index($4,"京")>0 {print $1, $4}' test