写在最开始
相比于sed,awk就显得强大多了。如果说sed是轻巧的山地摩托,那awk就是装甲坦克,分分钟把sed碾碎。awk的复杂性和高功能性在于:
- 支持扩展正则语法
- 语法规则更丰富和复杂,大量内置变量和灵活自定义变量
- 输出控制舒适,定制型极高
- 逻辑、流程、内置函数功能齐全
总之awk已经不能用命令来形容了,awk可以说是一门语言,专门为行数据处理而生,其高效性和机动性不言而喻。
记录和域
awk把输入行进行了结构化处理,通过制定分隔字符(用内置变量FS指定),把输入行(记录)分成若干个域,如下所示:
# input.txt
zhangsanfeng 70 1.87 wudang
zhangwuji 20 1.85 mingjiao
zhouzhiruo 18 1.65 emei
yuanzhen 40 1.73 shaolin
上面的文件中的每一行都是一条记录,单独拿出来一条记录时,又分为4个域,分别指代了:名字,年龄,身高,帮派,在awk命令中可以用$1 $2 $3 $4来引用每个域,用$0来表示这条记录(原汁原味,保留原来的所有字符),有了这些铺垫,我们就可以更方便的对每个域进行判断、计算、输出。
默认情况下分隔符是空格 TAB 或其以任意数量(至少1个)、任意次序的组合,即多个空格会被认为是一个分隔符。
输出和格式化
awk的输出方式和C语言几乎一样,语法和符号也几乎一致。总结起来有以下几点:
- 用print输出时,输出变量如果用逗号分隔,那么输出的变量间默认为一个空格符。
- 用print输出时,默认最后会输出一个换行符。
- 用print输出时,输出变量用空格分隔,那么输出变量间相互紧邻。
- 用printf输出时,输出格式完全由自己控制,最后也不会自动输出换行。
我们就上面的input.txt来进行一点展示。
图1.1中我么用逗号分隔要输出的变量$1 $2 $3 $4,输出结果自动会用空格符来分隔,如图1.2所示,不用逗号分隔输出结果就不会有分隔,而紧邻在一起。
如图1.3所示,我们在第2个域和第3个域间插入一个逗号,在第3个域和第4个域间插入一个下划线,在记录的结尾插入换行符,都能直观的在结果中展示。图1.4中展示了更漂亮的输出控制,符号-用于控制该域左对齐输出(默认右对齐输出),符号-后面的数字表示该域至少占的字符宽度(多了往后排,少了用空格补满),符号.和格式化符%f搭配,用于输出保留多少位的小数(会四舍五入)。
除了以上的格式化控制符,还有如下,仅把他列出来,不做一一分析。
%c:ASCII字符
%e:科学技术输出
%o:八进制输出
%x:十六进制输出
可以使用的变量类型
同sed一样,awk中变量也没有类型,也有两个值:整数值和字符值。我把awk中所有变量分为几类来分别说明:
- 普通变量(自定义变量)
在说明普通变量前,先来讲一下awk的基本结构。如下所示:
BEGIN {#这个左花括号必须和END在同一行,别问,问就规定,读入数据前执行一次。
}
{
# 对于每条记录,都要经过这个主体
}
END {#这个左花括号必须和END在同一行,别问,问就规定,处理完所有数据后执行一次。
}
由上面可以得知,awk执行的时候,大致可以分为三个阶段:BEGIN,主体,END。BEGIN最先执行,END最后执行,且都只执行一次。主体部分对于每行记录都要执行一次。
自定义变量推荐放在BEGIN中定义,这样比较清晰。如下所示,计算所有人的年龄的平均值。
上图的total就属于自定义变量,使用名字就可以直接引用。至于上面的NR就是我们接下来要讲的内置变量了。
- 内置变量
除了可以自定变量,awk实际还内置了很多变量,一来方便用户使用,二来,awk自己执行的时候也需要频繁用到这些变量,所以在定义自己的变量时,命名注意别和内置变量相同。内置变量的使用上和普通变量毫无差别。主要有如下常用的内置变量:
$0 1 2...:引用记录的域
FILENAME:记录来自哪个文件
FNR:浏览文件的记录数
NR:当前记录数,好像和FNR一样,number of record
FS:域分隔符,默认空格或TAB或其任意组合,field separation
OFS:输出分隔符,默认空格,output field separation
NF:当前记录中的域数量,number of field
ARGC:命令行参数个数
ARGV:命令行参数存放的数组,从0起
如下图所示:域分隔符被设置为字符h,输出分隔符为___,第一条记录:
z angsanfeng 70 1.87 wudang
被分隔为两个部分,所以在输出$3的时候没有任何东西。第三条记录:
z ouz iruo 18 1.65 emei
被分隔为三个部分,输出的时候,每个部分用OFS=___连接。
- 数组
awk中的数组的索引可以是字符串,也可以是数字,并且如果数字09和字符串"09"索引到的是不同元素。其实awk中的数组更像键值可以是任意类型的map。awk中提供了一种访问顺序未知的数组遍历方式:
for (i in arr)
{
#do something,i is the key,not the value
}
if ("zhangsanfeng" in arr)
{
#do something
}
更多情况,我们用数组是用来处理字符串,比如在函数split中,对记录进行分割后,分割的字段就存放在数组中,下标从1起。
- 命令行参数
除了内置变量和自定义变量,用户还可以通过命令行来传递参数给脚本,如下所示:
数据处理函数
- gsub:替换字符串
- length:返回字符串的长度
- match:检测是否包含匹配式