分类: Python/Ruby
2010-01-12 17:07:01
Awk one-liners explained(3)
终于到了基础部分的最后一篇,这篇内容不算多,加油吧。
第三篇:有选择的打印行
45、打印文件的前5行(相当于head -5)
#more file a b c d e f g h i j k l m n o p q r s t u v w x y z # awk 'NR < 6' file a b c d e f g h i j k l m n o |
前面也提到过,NR的意思是只当前文件的行号,每读一行,awk会自动给NR增加1。这个命令只有一个模式NR <6,而没有{动作},默认为{print $0}。模式中指的是匹配行号NR<6(即第一到第五行),即把文件file的第一到第五行打印出来。从第六行开始,awk检测到NR>=6,因此不执行动作{print}。
这个程序的效率不高,因为当NR>=6时,awk会继续遍历文件直到结束,但其实后面做的都是无用功,因为不执行任何动作。以下是改进的程序
# awk '1; NR == 5 { exit }' file a b c d e f g h i j k l m n o |
语句NR==5{exit}能够确保文件遍历到第五行的时候立刻停止程序。当文件的NR小于5的时候,语句“1”相当于{print},因此当NR<=5的时候,awk打印出所有匹配的行,然后程序停止。当文件的行数非常多的时候,提高执行的效率。
46、打印文件的第一行(head -1)
# awk 'NR > 1 { exit }; 1' file a b c |
和前面的例子一样,只有当NR==1的时候模式才匹配,读第一行的时候,模式不匹配,直接到第二个语句“1”,也就是{print},打印第一行,读到第二行的时候NR=2,匹配模式,执行{exit},退出了awk,完毕。
47、打印文件的倒数2行(tail -2)
# awk '{ y=x "\n" $0; x=$0 }; END { print y }' file v w x y z |
这个awk程序时怎么样工作的呢,首先看第一个语句{ y=x "\n" $0; x=$0 },这个语句只有动作没有模式,因此对所有行都执行动作。读取第一行的时候,变量y被赋值为”\nline1”(因为x没有被定义),然后定义变量x为”line1”. 读取第二行的时候,变量y被赋值为”line1\nline2”,变量x=line2。依次类推,读取第三行的时候y=”line2\nline3”,x=”line3”,一直到读取最后一行的时候,y=”lineN-1\nlineN”,x=”lineN”。这样变量y就包含了最后两行的内容,通过END{print y}将其打印出来。
仔细想想这个程序,大家能够看得出来这个程序的效率也是非常低的。Awk程序读取了整个文件的所有行,但是最后的动作无非是打印出来最后两行。不行的是在awk中没有像seek()这样的函数,因此无法在文件中找出最后两行(用tail可以找出),因此如果要打印出文件最的最后几行,还是把这个任务交给tail吧。
48、打印文件的最后一行(tail -1)
# awk 'END { print }' file y z |
这个程序有可能会生效也有可能不生效。它只有在文件遍历完以后,包含最后一行记录的$0没有被初始化的情况下才能生效。END{}模块是在所有记录都读取完以后执行,这里的{print}原意是想打印出文件结尾的$0,而这个变量是有可能被重置的。
而这个程序生效与否取决于你的awk的版本和执行过程,GNU的awk是可以执行成功的,但是在nawk 或者xpg4/bin/awk下貌似不能执行成功
PS:我用nawk和xpg4/bin/awk试过了,确实不会生效,也就是文件遍历完以后,$0被重置了。
49、打印匹配正则表达式/regex/的行(相当于grep的作用)
#awk '/f/' file d e f |
这个程序使用正则表达式/f/作为模式部分,匹配正则表达式则执行默认的动作{print},即打印匹配f的所有行。
50、打印不匹配正则表达式/regex/的行(相当于grep -v)
# awk '!/f/' file a b c g h i j k l m n o p q r s t u v w x y z |
正则表达式可以在表达式前面加上!来取反,匹配f的行在!的作用下变成了假,而不执行{print}的动作,相反不匹配f的行在!的作用下为真,执行动作{print},打印记录。
51、打印匹配正则表达式/regex/所在行的前一行。
# awk '/f/ { print x }; { x=$0 }' file a b c |
这个程序总是把当前的行赋值给变量x,直到有行匹配f,这时候打印变量x,而这个x正好是上一行的$0.因此打印出上一行。
如果当正则表达式匹配第一行的时候,这个程序就不起作用,因为没有前一行,x没有被定义。这时候我们可能会想要打印出”match on line1”(匹配第一行),可以用下面程序实现
# awk '/a/ { print (x=="" ? "match on line 1" : x) }; { x=$0 }' file match on line 1 |
这个语句测试变量x是否被定义,只有第一次使用x的时候x才为空,这样的话讲打印出”match on line1”。否则将打印出变量x的值,也就是读取上一行的时候被赋值给x的$0。
注意到awk中使用了一个三元运算符(x==””?”match on line1”:”x”)。它的意思是if(x==””),前面print的就是”match on line1”,不为空则print x
PS:三元运算符(a?b:c)是从c中借鉴过来的,意思是if a 为真,则b,else c。
52、打印匹配正则表达式/regex/所在行的下一行。
#awk '/a/ { getline; print }' file d e f |
这个awk程序在匹配/a/的所有行调用了getline()函数,getline获取的是当前行的下一行记录$0(同时更行变量NF,NR,FNR等),然后{print}打印出来的也是下一行的$0,这样就实现了打印匹配行的下一行。
如果刚好是最后一行匹配/a/,那么getline应该会返回一个错误代码,应为已经没有下一行了,但是实际上getline得到就是最后一行并且print出来
53、打印匹配/a/,/f/,/k/的行
# awk '/a|f|k/' file a b c d e f j k l |
这个语句的特点在于使用了扩展的正则表达式,意思是任意行只要匹配a,或f,或k,都将执行默认的{print}动作,打印出相应的行。
54、打印出包含a,b,c的行(顺序必须是a,b,c)
# awk '/a.*b.*c/' file a b c |
这个语句同样是在正则表达式上做手脚,模式是这样理解的,匹配的行中包含一个a,然后是任意字符(也可以是空),然后是b,然后是任意字符,然后是c,最后打印出这样的行。
55、打印长度大于64个字符的行
# awk 'length > 64' file |
模式部分使用的是length()函数,完整的模式应该是length(var),函数的作用是返回字符串var的长度,如果不指定var,默认将使用$0。因此这个语句在模式部分判断是否有超过64个字符的行,如果有,打印出来,没有则不执行任何动作。
56、打印长度小于64个字符的行
#awk 'length < 64' file a b c d e f g h i j k l m n o p q r s t u v w x y z |
这个没什么好讲的,大家肯定都能理解。
57、打印出从匹配正则表达式的行到文件末尾的部分区域
# awk '/s/,0' file s t u v w x y z |
这个awk程序使用的匹配模式是”模式1,模式2”这种形式,意思是从模式1匹配的行开始,一直到模式2匹配的行结束,对区间内所有的行执行{动作}。在这个例子中,模式1,是指包含s的行,模式2是0,也就是假,所以这例子也就是说从包含s的行开始,一直到文件结束,把这些行打印出来。
PS:由于0是假,不可能被匹配,因此模式匹配的行是从包含s的那行直到文件结尾
58、打印从第三行到第六行
# awk 'NR==3,NR==6' file g h i j k l m n o p q r |
这个例子同样使用了多个模式匹配的形式,模式1和模式2分别为NR==3,NR==6,即第三行和第六行,因此匹配3到6行的记录为3,4,5,6行,打印出来。
59、打印第五行
# awk 'NR==5' file m n o |
这个例子测试当前行是否是第五行,如果是第五行则打印出来。
其实正确的方法应该是匹配到第五行以后要立即停止awk程序,提高运行效率。
# awk 'NR==5{print;exit}' file m n o |
这个程序在遍历到第5行的时候,把第五行打印出来,然后强制退出awk,省去了读取其他行的时间,提高了效率。
60、打印从匹配正则表达式1的行到匹配正则表达式2的行内的所有行。
# awk '/a/,/k/' file a b c d e f g h i j k l |
前面讲过这个模式匹配的是从包含a的行到包含k的行,之间所有的行。然后打印出来。
PS:有个地方需要注意,文件中可能有多个区间都匹配这个模式,比如
e a b k a c k |
这种情况下的输出为
a b k a c k |
如果文件为
e a b k s a b |
输出为
a b k a b |
这是因为当模式第二次匹配到a的时候,下面没有行再匹配k,默认匹配到文件结尾。
61、删除文件中所有的空行
#more file a b c
d e f g h i
j k l m n o # awk NF file a b c d e f g h i j k l m n o |
这个例子使用了域的个数(NF)作为模式部分,因为空行中域的个数为0,因此不执行默认的{print}动作,还有一个命令可以实现这个功能
# awk '/./' file a b c d e f g h i j k l m n o |
这里使用的是正则表达式/./,它匹配的是任何包含字符的行,空行不包含字符,所以不被打印。
Awk的基本3部曲到这里就结束了,希望对新手们有所帮助。。谢谢了