awk入门及进阶

awk是Linux下优良的文本处理工具,有自成一体的编程语法规则,在循环和条件判断语句等方面和C语言很像。

其一般形式是:Pattern1 {ACTIONS; } Pattern2 { ACTIONS; },其中Pattern可以理解为条件(类似if语句),当满足相应条件时,执行相应的ACTIONS。

awk的功能十分强大,涉及的内容也非常广泛,以下仅记述笔者认为最为重要和常用的内容。 

1、关键内置参数和指令块

内置参数:

FS:输入字段分隔符,默认是空格

OFS:输出字段分隔符,默认也是空格

NF:每一行 ($0) 拥有的栏位总数

NR:所处理的是总共『第几行』

FNR:所处理的是当前文件『第几行』

注意:只有一个文件时,FNR与NR相等。

指令块:

BEGIN{}:在按行处理文件之前执行BEGIN块内指令,一般用来初始化相关参数。

END{}:在按行处理文件之后执行END块内指令,一般用来输出最终结果。 

2、理解按行处理

使用awk时,要明确{ACTIONS}对应的是每一行的处理动作;而且更加强大的是,各行的信息可以保留在变量里,供后续使用。

示例(a):将passwd文件内容作为输入,FS=":"指定分隔符为冒号;当第三个字段$3<10时,执行打印$1和$3的任务。

cat /etc/passwd | \
awk 'BEGIN {
FS=":"
}
$3 < 10 {
print $1"\t " $3
}'
示例(b):计算销售文件xs中的销售总金额:假设销售金额在记录的第三个字段,总金额保存在变量total中,最后打印出销售金额总计。

$awk -F ":" '{
print $3; #打印每一行的销售金额
total=total+$3;
}
END {
printf "销售金额总计:%.2f",total #打印总销售金额
}' xs

3、数组的使用

个人觉得数组是awk中最有用处的数据结构,大大提升了awk的效能。awk中的数组的下标可以是数字或字母,称为关联数组,能够辅助计数、存储,类似于python中的字典类型。

ü  用变量作为数组下标。如:$ awk{name[x++]=$2};END{for(i=0;i<NR;i++) print i,name[i]}' test。数组name中的下标是一个自定义变量x,awk初始化x的值为0,在每次使用后增加1。第二个域的值被赋给name数组的各个元素。在END模块中,for循环被用于循环整个数组,从下标为0的元素开始,打印那些存储在数组中的值。下标是关健字,它不一定从0开始,可以从任何值开始。

ü  for 循环用于读取关联数组中的元素。格式如下:

{
for (item in arrayname){
print arrayname[item]
}
}
#使用数组遍历输出所有以tom开头的行。
$ awk'/^tom/{
name[NR]=$1
};
END{
for(i in name){
printname[i]
}
}' test

ü  用字符串作为下标。如:count["test"]

ü  用域值作为数组的下标。一种新的for循环方式,for(index_value in array) statement。如:awk '{count[$1]++}END{for(name in count) print name,count[name]}' test。该语句将打印$1中不同字符串出现的次数。它首先以第一个域作数组count的下标,第一个域内容变化,索引就变化。

ü  delete 函数用于删除数组元素。如:$ awk '{line[x++]=$1} END{for(x in line)delete(line[x])}' test。分配给数组line的是第一个域的值,所有记录处理完成后,for循环将删除每一个元素。

应用举例:

使用数组合并两个文件,其中NR==FNR时处理前一个文件,NR>FNR时处理后一个文件。

awk -F ","  'BEGIN{
OFS=","
}
NR==FNR{
a[$1]=$0 # $1为第一个文件中的userid字段
}
NR>FNR && NF>3{ #行字段大于3个的行
gsub("","",$3);
if(a[$3]!="")print $0,a[$3] #$3为第二个文件的userid字段
}' file1 file2

4、应用正则匹配

使用awk处理文件时,常常有这样的需求:当行中包含某一字符串时,就进行相应的操作。正则匹配的原理简单,但功能相当强大,以下直接使用范例来说明。

范例参考:http://www.cnblogs.com/zhuyp1015/archive/2012/07/14/2591822.html

假设有这样一个待处理的文件"grade.txt":

M.Tansley         05/99        48311       Green       8       40     44

J.Lulu        06/99        48317       green        9       24     26

P.Bunny    02/99        48     Yellow       12     35     28

J.Troll        07/99        4842         Brown-3   12     26     26

L.Tansley  05/99        4712         Brown-2   12     30     28

#非精确匹配,第3个字段包含“48”时则打印
$ awk '$3 ~/48/ {print $0}' grade.txt
#精确匹配,第3个字段等于48时则打印
$ awk '$3=="48" {print $0}'grade.txt
#不区分首字母的大小写,匹配含Green或者green的行
$ awk '/[Gg]reen/' grade.txt
#匹配第一个域的第四个字符是‘a’的行
$ awk '$1 ~/^...a/' grade.txt
# “或”匹配,使用‘|’
$ awk '$0 ~/(Yellow|Brown)/' grade.txt
#也可以这样,不加括号
$ awk '$0 ~/Yellow|Brown/' grade.txt

5、格式化输出

大多数情况下使用print就能解决问题,但有时候我们需要使用格式化输出,awk中的printf()和sprintf()都可以进行格式化输出,语法规则几乎与C语言完全相同。

printf()将字符串打印到stdout。在使用printf()时,不会像print()一样自动打印换行符,需要自己在字符串末尾添加"\n"符号。

# cat grade.txt | \
awk 'NR==1{printf "%10s %10s %10s %10s%10s\n",$1,$2,$3,$4,"Total" }

sprintf()返回可赋值给变量的格式化字符串。

根据 Format 参数指定的sprintf 子例程格式字符串来格式化Expr 参数指定的表达式并返回最后生成的字符串。

sprintf(Format, Expr, Expr, . . . )
cat grade.txt |awk'NR==1{a=sprintf("%10s\n",$3); print a}'

6、shell变量的引用

awk中不能像引用自身变量一样引用shell变量,可用的引用方式如下:

(1)使用引号"'$var'"

这种写法大家无需改变用'括起awk程序的习惯,是老外常用的写法.如:

var="test"
awk 'BEGIN{print "'$var'"}'

(2)使用-v选项

假设shell中有:var="this is a test",则在awk中可以这样引用:

awk –v nvar="$var" 'BEGIN{print nvar}'

7、增加代码可读性

有些人习惯将所有的awk语句写到一行,而且没有注释;如果只是要实现简单的功能,这样没什么问题,但是当需要实现的功能比较复杂时,则这样操作不利于代码的阅读、维护和重用。

关于增加代码的可读性,有以下几个方面值得注意:

1)使用注释:与bash一样,awk的注释符号也是“#”。

2)使用缩进和换行:在awk中换行和缩进都不会影响代码的执行,而且好的排版可以大大增强可读性。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值