logo资料库

Awk one-liners explained 中文版.pdf

第1页 / 共25页
第2页 / 共25页
第3页 / 共25页
第4页 / 共25页
第5页 / 共25页
第6页 / 共25页
第7页 / 共25页
第8页 / 共25页
资料共25页,剩余部分请下载后查看
Awk one-liners explained 中文版 http://bbs.chinaunix.net/thread-1640657-1-1.html 首先声明,这篇文章并不是原创,这是我在学习 awk 的过程中,经 CU 的朋 友推荐,看到了 Peteris Krumin 关于 awk 的非常精彩的讲解,由于原文是英文版 的,英语水平稍差的朋友可能学习起来会有点困难,为了能够给正在学习 awk 的朋友们提供一点点帮助,也锻炼一下自己的英语水平,我将这些 awk 讲解翻 译了一下,加上了一点个人的看法,由于本人水平有限,错误肯定不少,欢迎高 手们批评指正,共同进步。谢谢。 http://www.catonmat.net/blog/awk-one-liners-explained-part-one/ 这 是 原 文 的 链 接。。 废话不说,开始进入 awk 第一个部分:行距,编号和运算。 第一部分:行距,编号和运算 1、输出两倍行距文件 #more file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 #awk '1; { print "" }' file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 这个是如何运行的呢?每一个 awk 程序都是由一系列的 “模式—动作”语句组 成,即“模式{动作}”,在这个例子中有两个这样的语句,即"1" 和 "{ print "" }", 每个语句中无论是模式还是动作都可能不存在。如果模式部分不存在,默认匹配 所有行,那么就对每一行都执行动作。如果动作不存在,默认执行动作{print}, 因此这个例子也可以写成这样: #awk '1 { print } { print "" }' file 只有当模式正确匹配,动作才会执行,由于“1”始终是正确的,所以这个例子 可以写成两条打印的语句 #awk '{ print } { print "" }' file 在 awk 中每一条打印命令中后面都跟了一个输出记录分隔符(ORS),默认是换
行。第一个打印命令后面没有加参数,等同于{print $0},$0 是相应的每一条记录, 是可变的,通过设置输出记录分隔符(ORS)可以得到不同类型的记录。第二个 打印命令后面接的是“”(空),我们知道每个打印命令后面都跟了一个输出记录 分隔符,实际上打印了一个新行,因此打印出来以后每行之间有双倍的行距。 PS:例子中的分号的作用是将两条语句区分开来,如果不用分号 awk 会认为是 一个语句。其他两种写法不加分号是因为检测到有动作{print}存在,后面的内容 会自动认为是另一个语句。所以在 awk 中,当前一个语句只有模式而没有动作 时,后面要在加语句的话,必须要用分号区分开来,有动作时分号可有可无。 2、另一种方法输出两倍行距文件 #awk 'BEGIN { ORS="\n\n" }; 1' file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 BEGIN 是一种特殊的语句,这种语句不检测输入文件。也就是在读输入文件之前 运行。通过设置输出记录分隔符(ORS)为两次换行的方法实现输出两倍行距文 件。根据之前提到过的,语句“1”等同于{print},打印出来的每条记录之间的分 隔符都为前面设置的 ORS。 PS:一个完整的 awk 语句应该是这种形式 awk ‘BEGIN{动作};模式{动作};模式{动作}。。。;END{动作}’ file 其中 BEGIN{动作}和 END{动作}分别是在读输入文件之前和读输入文件之后执行, 通常用来制表和统计数据,很多时候都不必用到。只有中间的语句模式{动作}才 会读输入文件并对其执行动作。 3、输出两倍行距文件,并且任意两行之间只有一个空行存在。 首先修改一下 file,在里面加一个空行 #more file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 #awk 'NF { print $0 "\n" }' file 12 32423 -2354235 234532 23456 555555
-432346 45435 4462 这个命令使用了另一个 AWK 变量 NF(域的个数),意思是当前行被分割的数量, 比如 234532 23456 555555 这一行被分割成 3 个部分,因此得 NF 的值就是 3,空行的无法被分割,因此 NF 的大小就是 0.在模式中使用 NF 可以有效的过滤空行。这个命令的意思是:只要 行中存在域,就在此行的后面打印一个空行。 PS:当模式为数值(-1,0,1,1.1)时,只要数值不为 0,即为匹配所有行。 4、 三倍行距 #awk '1; { print "\n" }' file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 这个命令跟之前的很是相似,语句“1”等同于{print},因此也可以写成 #awk '{ print; print "\n" }' file 先打印一行,然后是打印输出记录分隔符(ORS),默认是换行。 PS:有些初学者在这里可能会有点疑惑(我刚开始也想了很久 O(∩_∩)O),不 过仔细想想就容易理解了。之前我们提到过,每个打印命令后面都跟了一个输出 记录分隔符(默认是换行),因此在这个命令中,先执行第一个语句:首先打印 文件的第一行,然后跟一个 ORS,也就是换行了,接着执行第二个语句{print “\n”}, \n 就是换行,这时候后面又跟一个 ORS,还是换行,因此出现了在每行之间出现 了 2 个空行。 5、给每个文件的行单独编号 #awk '{ print FNR "\t" $0 }' file 1 12 32423 -2354235 2 234532 23456 555555 3 -432346 45435 4462 这个 awk 程序在每行之前附加了一个文件行号(FNR)file line number 和一个 tab (\t),FNR 包含了每一个文件当前行的行号。比如说,awk 针对两个文件做操作:
#awk '{ print FNR "\t" $0 }' file file 1 12 32423 -2354235 2 234532 23456 555555 3 -432346 45435 4462 1 12 32423 -2354235 2 234532 23456 555555 3 -432346 45435 4462 可以看到,结果是分别给两个文件的每一行之前加上该行在文件中的行号。FNR 给文件的行编号时,如果有多个文件会重新开始编号。 6、给所有文件的行一起编号 #awk '{ print NR "\t" $0 }' file file 1 12 32423 -2354235 2 234532 23456 555555 3 -432346 45435 4462 4 12 32423 -2354235 5 234532 23456 555555 6 -432346 45435 4462 这个命令和第五个例子几乎一样,唯一不同的地方是使用了参数(行号)NR-Line Number。NR 与 FNR 不同的地方就在于 NR 在给多个文件的行编号的时候不会根 据文件重新编号,而是按照读取顺序统一编号。 7、花式编号 #awk '{ printf("%5d : %s\n", NR, $0) }' file 1 : 12 32423 -2354235 2 : 234532 23456 555555 3 : -432346 45435 4462 这个命令用了通常格式 printf()函数来给给行编号,像普通的 printf()函数一 样格式化参数。这里需要特别注意的是在 printf()函数后面不会附加一个输出 记录分隔符(ORS)。因此我们需要在每一行的后面明确的打印出一个换行符(\n)。 这个命令的结果是在每行之前打印行号和一个冒号。 8、只给非空行编号 #more file 12 32423 -2354235 234532 23456 555555 432346 45435 4462
#awk 'NF { $0=++a " :" $0 }; { print }' 1 :12 32423 -2354235 2 :234532 23456 555555 3 :-432346 45435 4462 Awk 参数都是动态的,在第一次使用的时候建立。这个命令指定 a 这个变量随着 行数的增加和不断自增长,空行除外(NF=0)。然后将这个参数的值和冒号附加 在每一行的开头并打印出来。 PS:在这个例子中,第一个语句是 NF{$0=++a”:”$0},模式是 NF,动作是一个 赋值语句$0=++a”:”$0,即当 NF 不为 0 的时候,在每一行的开头都加上一个变 量 a 和一个冒号,然后通过第二个语句打印出来。记住,在这里“=”不是等于 的意思,而是给$0 重新赋值,“==”才是等于的意思。 9、计算文件行数(与 wc –l 的作用类似) #awk 'END { print NR }' file 4 前面提到过,END{}是一种不测试文件的特殊语句,它是在所有行遍历完以后执 行。在所有行遍历完以后,NR 就等于最后一行的行号,再出变量 NR 的值,就 是输出文件的行数了。 10、打印每行中域值的总和 #awk '{ s = 0; for (i = 1; i <= NF; i++) s = s+$i; print s }' file -2321800 813543 -382449 awk 有很多地方借鉴了 c 的风格,比如这个 for(;;){„}循环。这个命令使用 for 循环遍历了每一行中的每个域(NF 即为每行中域的个数)。然后将每个域的值累 加给变量 s,最后打印出 s 的值,即为所有域相加后的值。 PS:如果把 s=s+$i 写成 s=s+i,则结果大相径庭 #awk '{ s = 0; for (i = 1; i <= NF; i++) s = s+i; print s }' file 6 6 6 因为前面的 s=$1+$2+$3,而后面的 s=1+2+3。 11、打印所有行中域值的总和 #awk '{ for (i = 1; i <= NF; i++) s = s+$i }; END { print s+0 }' file -1890706
这个命令基本上和#10 的一致,不同的是打印出来的结果是所有域值的累加。注 意到开始没有初始化变量 s 的值为 0.这是因为要将所有行的每一个域相加的话, 就不能在遍历每一行的时候将 s 初始化为 0,否则最后得到的 s 就是最后一行的 所有域相加的值,而不是所有行。还需要注意的是最后是{print s+0}而不是{print s}。 当文件 file 中没有域(都是空行)的时候这是很有必要的。因为如果没有域的话, 那么变量 s 就无法建立也没有被定义,输出一个没有定义的变量就等于什么都不 输出(ORS 还是要跟的)。加上一个 0 的话就可以从数值上体现 s 的大小即为 0。 12、将所有域都替换成它的绝对值 #awk '{ for (i = 1; i <= NF; i++) if ($i < 0) $i = -$i; print }' file 12 32423 2354235 234532 23456 555555 432346 45435 4462 这个命令也借鉴了 c 的两个特征,if(..){„}语句和省略了大括号。这个命令遍历 了所有行中的每一个域,检查是否有小于 0 的域,如果有,将域值取反,变成整 数。域值可以使用参数间接的赋值,比如 i=5;$i=”hello”,就是将第五个域赋值 为”hello” 下面是该命令的另一种比较完整的写法,在每行中所有的域都检查过并且重新赋 值以后,再执行打印的命令。 awk '{ for (i = 1; i <= NF; i++) { if ($i < 0) { $i = -$i; } } print }' 13、计算一个文件中所有域的数量 #awk '{ total = total + NF }; END { print total+0 }' file 9 这个 awk 程序遍历文件 file 所有的行,并将每一行的域的数量累加起来,并将累 加的数量赋给变量 total,一旦文件遍历完,开始执行 END{},也就是打印出变量 total 的值,通过第十一个例子我们可以知道为什么打印的是 total+0。 14、打印包含“55”内容的行的个数 #awk '/55/ { n++ }; END { print n+0 }' file 1
这个命令包含两个模式{动作}语句。第一个是/55/{n++}。模式中两个斜杠之间的 是一个正则表达式。表示匹配所有包含数字“55”的行(不一定精确匹配 55 这 个数字,也匹配像 555,553,255,55d 这种)。当匹配了一行时,变量就自动+1, 第二个语句是是 END{print n+0},表示当文件遍历完后,打印出变量 n 的值。注 意到是 print n+0,因为当没有行匹配 55 的时候,n 就没有被创建和被定义,n 的值也就打印不出来,加上 0 可以避免无输出。 PS:关于正则表达式有一篇文章介绍的不错: http://bbs.chinaunix.net/viewthread.php?tid=63273 可以参考一下。 15、找出第一个域中最大的数 #awk '$1 > max { max=$1; maxline=$0 }; END { print max"\n"maxline }' file 234532 234532 23456 555555 这个命令将通过比较将第一个域中的最大数保存在变量 max 中,并且把相对应 的行赋给变量 maxline,当所有行遍历完以后,把它们都打印出来。需要注意的 是,当第一个域中所有的域值都是负数时,这个程序无法工作。 PS:为什么第一个域都是负数的时候,打印不出来任何东西呢,是因为当第一个 域都是负数的时候,没有行能够匹配模式$1>max,因此后面的动作也就不能执 行,变量 max 和 maxline 没有被定义,因此打印不出任何东西了。 利用下面的命令可以弥补这一缺陷 #awk 'NR == 1 { max = $1; maxline = $0; next; } $1 > max { max=$1; maxline=$0 }; END { print max”\n” maxline }' file -12 -12 32423 -2354235 这个命令在前一个命令的基础上加了一个命令 NR==1{max=$1;maxline=$0;next}, 我们来看一下,模式部分是行号 NR==1,也就是说动作只对第一行做操作,把第 一个域的值赋给变量 max,第一行赋予变量 maxline。注意后面的 next 函数。next 意思就是:匹配 NR==1 的行执行完动作{max=$1;maxline=$0}后,后面的语句通通 不执行(END{}除外),也就是说后面的语句从 NR=2 开始执行。通过后面语句来 和第一行的$1 做比较,最后选出最大的$1 和相应的行并打印出来。 PS:新加的语句主要是用来初始化变量 max 和 maxline,即不管$1 的大小,直接 把$1 赋给 max,然后再比较。因此不会出现 max 没有创建和被定义的现象。 16、在每行之前打印出域的个数 #awk '{ print NF ":" $0 } ' file 3:-12 32423 -2354235 3:234532 23456 555555 3:432346 45435 4462
这个命令还是很简单了,先打印出预先确定的 NF-number of fields(域的数量),后 面加一个冒号和行记录。 17、打印每行的最后一个域 #awk '{ print $NF }' file -2354235 555555 4462 每一行域的数量 NF 不会总是一样,$NF 就是每行的最后一个域。 18、打印最后一行的最后一个域 #awk '{ field = $NF }; END { print field }' file 4462 这个命令将记录中的最后一个域赋给变量 field,所有行遍历完以后,变量 field 的值就是最后一行记录的最后一个域了,然后打印出变量 field。 有一个更好,更常用的写法 #awk 'END { print $NF}' file 4462 19、打印超过 4 个域的行 #awk 'NF > 4' file 这个命令省略了动作只有模式,缺省的动作就是{print $0},因此如果匹配到有超 过 4 个域的行,打印出来,如果没有,则无输出。 20、打印最后一个域值大于 4 的行 #awk '$NF > 4' file 234532 23456 555555 432346 45435 4462 和前一个例子不同的地方在于,这个模式匹配的是最后一个域值大于 4 的行。并 打印相关的行。 好了,第一部分内容写完了,比较基础的东西。 第二部分是文本转换和替代,下次继续! 第一部分的还是比较简单的,从第二部分开始,就要接触到很多函数和数组 的东西,由于本人只有一点点 c 的基础,所以为了不误人子弟,希望大家能够多 提意见。谢谢。
分享到:
收藏