一个利用sed和awk处理文本的小栗子
这两天做《Linux操作系统》课程的作业,碰到了一个题目,感觉很有意思,很考验对awk掌握的熟练度,故特意拿来分享。
首先说题目是这样的,有这样一段文本:
RECORD #这是多余的注释行one #record_type students #这是多余的注释行two F sno 11111110000 F name 王铁蛋 F gender 男 F age 20 F class 网络工程01 F region 湖北省武汉市 . RECORD #这是多余的注释行one #record_type scores #这是多余的注释行two F sno 1205110606 F mathematics 92 F english 88 F chinese 86 F history 79 F politics 83
然后要我们利用sed和awk,改啊改,改成这个样子:
这个怎么做呢,首先就是要去掉那个多余的注释行,那个很好去掉,就是利用sed的正则表达式判断就好了,以#开头的,且后面跟着的不是r这个字符,那么就把这行删除,具体命令如下:
这样就可以去掉多余的注释行了。
接下来我们就要进入主题了,利用awk来对文本进行处理。
根据结果来看,可以看到这个题目中awk其实一共录入了两个record,也就是说我们的记录分隔符RS不能再用回车了,得用原文本中的那个点,同时,每条记录中的每个域应该就是record行到“.”行的每一行,所以我们的域分隔符也应该换一下,变成回车。所以awkBEGIN时执行命令应该是这两条。
接下来就得要分析,如何打印开头的那个Record (1) “students”了,括号里面的那个数字应该是当前记录号,而后面的那个students应该是取自record_type那一行,这个具体要怎么做呢,其实很简单,如下:
用for循环分别取出每个域,然后判断,如果以#开头,那么则说明是#record_type 那一行,我们就对这一行利用gsub函数进行处理。
那个gsub函数是干嘛的呢,其实就是一个替换(substitute)函数,它利用第一个参数里的正则表达式来对第三个参数进行查找,将查找到的内容用第二个参数进行替换。具体到本栗子中就是,查找以#开头空格结束这么一段内容,然后用空字符串进行替换。这样其实就是做了一个删除的功能。这样处理之后,#record_type students就变成了students了,然后我们再来进行输出就好了,需要注意的是,print函数类似于c语言的printf函数,可以直接对字符串进行连接,所以上面那个print第二个参数其实就是输出了“students”。
输出了头之后,我们就该输出中间的内容了,我们可以很容易地发现一个规律就是所有内容都是以F开头的,所以这里我们就可以这样处理。
首先判断这个域的内容是不是以F开头的,如果是,则进行处理。
中间那个split函数的功能就是进行分割,对$i字符串的内容以第三个参数空格进行分割,然后将分割的结果存放到第二个参数所代表的数组中,然后再来将内容输出就好了。
整个for循环结束之后,再来打印一遍这条记录结束了,这样就能实现我们的功能了。
最后再把代码整体贴一遍,希望对各位有所帮助:
1 #!/bin/bash 2 #History: 3 # Michael 4月,27,2015 4 5 sed '/#[^r].*/d' recordmdA.txt | \ 6 7 awk ' 8 BEGIN { 9 RS="." 10 FS="\n" 11 } 12 { 13 for(i=1;i<=NF;i++) 14 { 15 if($i ~ /#.*/) 16 { 17 gsub(/#.* /,"",$i) 18 print "Record ("NR")","\""$i"\"" 19 } 20 else if($i ~ /F.*/) 21 { 22 split($i,A," ") 23 print "\t""\""A[2]"\"""=""\""A[3]"\"" 24 } 25 } 26 print "End of Record ("NR")" 27 } 28 ' 29 30 # gsub函数的功能就是通过第一个参数的正则表达式在第三个参数中查找特定字符串,然后用第二个参数来替换这个字符串,具体到本例中就是将#record_type score字符串中空格前面的内容全部删掉 31 32 # split函数的功能就是分割,通过第三个参数来将第一个参数的内容进行分割,并将分割的结果存放到第二个参数所代表的数组中