免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 23271 | 回复: 28

[文本处理] awk之选择性打印 [复制链接]

论坛徽章:
2
射手座
日期:2014-10-10 15:59:4715-16赛季CBA联赛之上海
日期:2016-03-03 10:27:14
发表于 2011-02-17 11:09 |显示全部楼层
本帖最后由 yinyuemi 于 2016-11-14 09:14 编辑

最近的帖子中有不少是关于选择性打印的,自认为用awk来处理这样的文本是有很大的优势,而且很容易理解。
在处理这类问题时,可以通过设置一些变量作为打印标签,达到选择的目的。
另,还可以利用awk自身的函数如NF,RS,RT,FS等,有时会达到意想不到的效果。

下面是就自己的一些理解做了下总结,如有不对之处,望不吝指正!

1. 按照某个pattern取其前后几行数据:
  1. cat file
  2. aaa
  3. bbb
  4. ccc
  5. ddd
  6. eee
  7. fff
  8. ggg
  9. hhh
复制代码
1.1 pattern等于ddd时,取其前1行:
  1. awk '!/ddd/{m=$0}/ddd/{print m}' file
  2. ccc
复制代码
如果是取其前n行,需要借助下数组,以前3行为例
  1. $ awk '{a[NR]=$0}/ddd/{p=NR}END{for(i=p-3;i<p;i++) printf a[i] RS}' file
  2. aaa
  3. bbb
  4. ccc
复制代码
1.2 pattern等于ddd时,取其后n行,这个就要简单的多,以后3行为例
  1. awk '/ddd/{p=1;x=NR}p&&NR-x<=3&&NR-x>0' file
  2. eee
  3. fff
  4. ggg
复制代码
这就可以用awk实现,grep -A /grep -B 的功能。

1.3 如果对pattern出现的次数有要求时,第二次匹配aaa时取后2行
  1. cat file

  2. aaa
  3. bbb
  4. ccc
  5. aaa
  6. 111
  7. 222
  8. aaa
  9. 333
  10. 444

  11. awk '/aaa/{m++;x=NR}m==2&&NR-x>0' file
  12. 111
  13. 222
复制代码
2. 按照某2个pattern,取其间数据
  1. cat file
  2. aaa
  3. bbb
  4. ccc
  5. ddd
  6. eee
  7. fff
  8. ggg
  9. hhh
复制代码
如,匹配取匹配bbb和fff之间的数据

2.1 包括匹配行
  1. awk '/bbb/{p=1}/fff/{print;p=0}p' file
  2. bbb
  3. ccc
  4. ddd
  5. eee
  6. fff
复制代码
2.2 不包括bbb行
  1. awk '/bbb/{p=1;next}/fff/{print;p=0}p' file
  2. ccc
  3. ddd
  4. eee
  5. fff
复制代码
2.3 不包括fff行
  1. awk '/bbb/{p=1}/fff/{p=0}p' file
  2. bbb
  3. ccc
  4. ddd
  5. eee
复制代码
2.4 不打印匹配行
  1. awk '/bbb/{p=1;next}/fff/{p=0}p' file
  2. ccc
  3. ddd
  4. eee
复制代码
3. 删除或保留含有某个pattern的数据块,看个实例吧:
  1. cat file

  2. DFDG
  3. START
  4. DSFDS
  5. DSDS
  6. XXX
  7. END
  8. AADD
  9. START
  10. SDSD  (VIO)
  11. FGFG
  12. END
  13. START
  14. DSFDS
  15. DSDS
  16. XXX
  17. END
  18. START
  19. adad (VIO)
  20. gfthgh
  21. END
复制代码
要求是保留从START 到 END 之间含有(VI0)的数据。

代码1:
  1. awk '/START/{p++}/./{a[p]=a[p]"\n"$0}END{for(i=1;i<=p;i++) if(a[i]~/\(VIO\)/) print a[i]}' file

  2. START
  3. SDSD  (VIO)
  4. FGFG
  5. END

  6. START
  7. adad (VIO)
  8. gfthgh
  9. END

复制代码
这段代码将打印标签(p)作为数据下标,把每段从START到END之间数据存入数组中,最后打印匹配(VIO)的数组值。

代码2:
  1. awk -v RS="START" '/VIO/{print RS $0}' file
  2. START
  3. SDSD  (VIO)
  4. FGFG
  5. END

  6. START
  7. adad (VIO)
  8. gfthgh
  9. END
复制代码
这段代码巧妙地利用awk的内置函数RS。

再看另外一个例子:
http://bbs.chinaunix.net/viewthr ... &page=1#pid12687973

  1. test pool

  2. 001784  HCART3   ACS      3       -      -       15     91488   ACTIVE
  3. 001812  HCART3   ACS      3       -      -       15     94912   ACTIVE

  4. ypepivsdb1 pool

  5. 000045  HCART3   TLD      1       46     -       3     844233440        ACTIVE
  6. 000745  HCART3   TLD      1       455    -       3     3668000  ACTIVE
  7. 000823  HCART3   TLD      1       533    -       3     46102304 ACTIVE

  8. ypfpivsdb2 pool

  9. 000914  HCART3   TLD      1       624    -       3     73076448 ACTIVE
  10. 001004  HCART3   TLD      1       126    -       3     19219968 ACTIVE
  11. 0117L3  HCART3   TLD      2       118    -       3     101234816        ACTIVE
  12. 0121L3  HCART3   TLD      2       122    -       3     164481920        ACTIVE
复制代码
统计3个pool分别有几个磁带(HCAR)
  1. awk '/pool/{p++;b[p]=$0}/ACTIVE/{a[p]++} END {for(i=1;i<=p;i++) print b[i], a[i]}' file
复制代码
4.打印奇数偶数行
  1. seq 10 |awk ++p%2
  2. 1
  3. 3
  4. 5
  5. 7
  6. 9

  7. seq 10 |awk p++%2
  8. 2
  9. 4
  10. 6
  11. 8
  12. 10
复制代码
5.打印匹配pattern的行号

http://bbs.chinaunix.net/viewthread.php?tid=1354835&extra=&page=1
  1. cat test                                                                     
  2. ded
  3. aaaaa
  4. sssss
  5. 111
  6. dedwef
  7. dwefref
  8. aaaaa
  9. ddddd
复制代码
获取关键字“111”后出现的第一个aaaaa所处行的行号。
  1. awk '/aaaaa/{p=NR}/111/{x=NR}p>x&&x>0{print p;exit}' test

  2. 7
复制代码
扩展一下,如果是第n个呢,只要再加个标签就可以,以n=3为例,
  1. cat test
  2. ded
  3. aaaaa
  4. sssss
  5. 111
  6. dedwef
  7. dwefref
  8. aaaaa
  9. ddddd
  10. aaaaa
  11. xxxx
  12. aaaaa
  13. zzz
  14. aaaaa
  15. sss

  16. awk '/aaaaa/{p=NR}/111/{x=NR}p>x && x>0 &&/aaaaa/{n++}n==3{print p;exit}'

  17. 11
复制代码

论坛徽章:
0
发表于 2011-02-17 11:11 |显示全部楼层
回复 1# yinyuemi


    学习~~

论坛徽章:
0
发表于 2011-02-17 13:42 |显示全部楼层

论坛徽章:
0
发表于 2011-02-17 14:33 |显示全部楼层
1.1 pattern等于ddd时,取其前1行:
  1. awk '/ddd/{print i};i=$0{}'
复制代码
如果是取其前n行,需要借助下数组,以前3行为例
  1. awk '{a[NR]=$0}/ddd/{for(i=NR-3;i<NR;i++)print a[i]}'
复制代码
改动后能解决文件中多个匹配行到情况

1.2 pattern等于ddd时,取其后n行,
  1. awk '/ddd/{i=3;next}--i>=0'
复制代码
1.3 如果对pattern出现的次数有要求时,第二次匹配aaa时取后2行
  1. awk '/aaa/{i++;j=2;next};--j>=0 && i==2 '
复制代码
2.1 包括匹配行
  1. awk '/bbb/,/fff/'
复制代码

论坛徽章:
2
射手座
日期:2014-10-10 15:59:4715-16赛季CBA联赛之上海
日期:2016-03-03 10:27:14
发表于 2011-02-18 08:15 |显示全部楼层
回复 4# igi-cu


    Really nice!!!
   多谢这位兄弟,学习学习!

论坛徽章:
0
发表于 2011-02-18 08:35 |显示全部楼层
我认为,这个不是很好的方法,假设在到达匹配的行前,你需要查看大量的行,那么你的呵a数组将会非常大
,将线性数组存放改成环形数组会比较好点

  1. $ awk '{a[NR]=$0}/ddd/{p=NR}END{for(i=p-3;i<p;i++) printf a[i] RS}' file
复制代码

论坛徽章:
2
射手座
日期:2014-10-10 15:59:4715-16赛季CBA联赛之上海
日期:2016-03-03 10:27:14
发表于 2011-02-18 08:55 |显示全部楼层
回复 6# justlooks


    恩,的确是,
对igi-cu的代码改动了一下,速度会更快
  1. awk '{a[NR]=$0}/ddd/{for(i=NR-3;i<NR;i++)print a[i];exit}'
复制代码
  1. time seq 1000000 |awk '{a[NR]=$0}/\<100000\>/{for(i=NR-3;i<NR;i++)print a[i]}'
  2. 99997
  3. 99998
  4. 99999

  5. real    0m2.555s
  6. user    0m2.883s
  7. sys     0m0.166s

  8. time seq 1000000 |awk '{a[NR]=$0}/\<100000\>/{for(i=NR-3;i<NR;i++)print a[i];exit}'
  9. 99997
  10. 99998
  11. 99999

  12. real    0m0.194s
  13. user    0m0.273s
  14. sys     0m0.009s
复制代码

论坛徽章:
0
发表于 2011-02-18 09:50 |显示全部楼层
回复 7# yinyuemi

嗯,确实

论坛徽章:
0
发表于 2011-04-19 11:01 |显示全部楼层
学习了 楼主对awk理解很深刻

论坛徽章:
23
15-16赛季CBA联赛之吉林
日期:2017-12-21 16:39:27白羊座
日期:2014-10-27 11:14:37申猴
日期:2014-10-23 08:36:23金牛座
日期:2014-09-30 08:26:49午马
日期:2014-09-29 09:40:16射手座
日期:2014-11-25 08:56:112015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:0315-16赛季CBA联赛之山东
日期:2017-12-21 16:39:1915-16赛季CBA联赛之广东
日期:2016-01-19 13:33:372015亚冠之山东鲁能
日期:2015-10-13 09:39:062015亚冠之西悉尼流浪者
日期:2015-09-21 08:27:57
发表于 2011-04-19 16:56 |显示全部楼层
回复 7# yinyuemi


a[NR]=$0 在文件很大时可能会抱内存不足的错误,所以处理这类问题更好的方法是“只记录你需要记录的内容”

利用取余数的方法,数组只记录3行记录:
  1. awk '/\<100000\>/{for(i=NR-3;i<NR;i++)print a[i%3];exit}{a[NR%3]=$0}'
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP