免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 24712236 | 回复: 24712236

[学习共享] awk初学之常见问题 [复制链接]

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

从初学awk到现在小有所成,非常感谢CUers的帮助,总结了下自己曾经遇到的问题和犯的错误,供初学者借鉴,因本人非计算机专业,对专业词汇可能有表述不对的地方,还请指正和补充!


1. awk ‘{code}1’ 中的“1”是干什么的?

一个完整的awk语句为:Awk ‘[patten]{action}……’, 其中pattern缺省为1,action缺省为{print}。

那么awk ‘1’完整的写法就是awk ‘1{print}’; 同理,awk ‘{print}’完整的写法也是awk ‘1{print}’。


2. NR和FNR的区别是啥?

NR: 当前行记录数。

FNR: 当前文件的行记录数。

当awk处理的文件数超过1时,NR和FNR才会有区别。例如:



  1. cat file
  2. a
  3. b
  4. c
  5. d
  6. e
  7. f

  8. awk '{print "NR = " NR "  FNR = " FNR, $0}' file
  9. NR = 1  FNR = 1 a
  10. NR = 2  FNR = 2 b
  11. NR = 3  FNR = 3 c
  12. NR = 4  FNR = 4 d
  13. NR = 5  FNR = 5 e
  14. NR = 6  FNR = 6 f

  15. awk '{print "NR = " NR "  FNR = " FNR, $0}' file file
  16. NR = 1  FNR = 1 a
  17. NR = 2  FNR = 2 b
  18. NR = 3  FNR = 3 c
  19. NR = 4  FNR = 4 d
  20. NR = 5  FNR = 5 e
  21. NR = 6  FNR = 6 f
  22. NR = 7  FNR = 1 a
  23. NR = 8  FNR = 2 b
  24. NR = 9  FNR = 3 c
  25. NR = 10  FNR = 4 d
  26. NR = 11  FNR = 5 e
  27. NR = 12  FNR = 6 f
复制代码


3. Awk怎么引入变量?

有两种方法:

<1>: awk -v var=$VAR '{code}'
<2>: awk '{CODE}'$VAR'{CODE}'
例如:


  1. VAR=XXX

  2. awk -v var=$VAR 'BEGIN{print var}'
  3. XXX

  4. awk 'BEGIN{print "'$VAR'"}'
  5. XXX
复制代码

我推荐使用第一种方法,这样可以避免一些不必要的烦恼。如http://bbs.chinaunix.net/thread-1835620-1-1.html


4. 为什么OFS不起作用?

先看一个例子:



  1. echo 'aaa bbb ccc ddd
  2. aaa bbb ccc ddd
  3. aaa bbb ccc ddd
  4. aaa bbb ccc ddd' |awk -v OFS="|" '{print $0}'
  5. aaa bbb ccc ddd
  6. aaa bbb ccc ddd
  7. aaa bbb ccc ddd
  8. aaa bbb ccc ddd
复制代码

上面的例子中OFS为什么没有生效呢,原因是OFS指的是输出字段分隔符,所以必须对字段进行操作时OFS才会起作用,正确的方法应该是:



  1. echo 'aaa bbb ccc ddd
  2. aaa bbb ccc ddd
  3. aaa bbb ccc ddd
  4. aaa bbb ccc ddd' |awk -v OFS="|" '{$1=$1;print $0}'
  5. aaa|bbb|ccc|ddd
  6. aaa|bbb|ccc|ddd
  7. aaa|bbb|ccc|ddd
  8. aaa|bbb|ccc|ddd
复制代码

正如Tim大师所讲的,$1=$1这个action,是我们对awk撒的谎,目的就是为了使得OFS生效,除此之外,NF+=0也是常用的方法。参考:http://bbs.chinaunix.net/viewthread.php?tid=1354674&extra=&page=1



5. 同样的代码,别人运行成功,为什么我运行失败?

这个问题的原因很多,我这里列举两个最常见的,大家可以补充。

<1>: awk版本引起的,如gawk中的一些扩展函数或变量,在nawk中没有,或是不同版本的(g/n)awk也会有差别,这样情况需要重新编写。

<2>: 文本格式的问题,cat-A file查看一下,如果是,dos2unix应该可以解决。

注:书写错误也有可能哦


6. Awk 语句中可以使用{n,m}这样的正则么?

可以,使用方法:gawk -- re-interval ,其它版本使用方法会有所不同,请大家补充


7. BEGIN 和END 到底是怎么一回事?

有时,对于新手可能也会是个问题。简单说下:

BEGIN {action} : 读取文本之前进行的操作。要避免类似下面的写法:



  1. awk 'BEGIN{ filename =  FILENAME}' file
  2. # or:
  3. awk 'BEGIN{FS=":"; for(i=2;i<=NF;i++) print $i}' file
复制代码


如果BEGIN 模块中使用getline函数时,情况会有所不同:

  1. cat file
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5

  7. awk 'BEGIN{while (getline <"file") print}' file
  8. 1
  9. 2
  10. 3
  11. 4
  12. 5

复制代码

END {action}:
它在整个输入文件处理完成后被执行,同样无法对文本进行任何操作,如匹配某个pattern执行action。


8. print,printf 和sprintf?

print:为一般的打印

printf:可以定义打印格式

sprintf:可以完成和printf相同的功能,不同的是sprintf只能输出值,并不能完成打印的功能。

12楼-expert1补充::) print默认有个换行\n,
而printf没有,当然它和C语言的printf类似(awk本是c的近亲),能打印各种格式,但默认没有换行。

例如:



  1. awk 'BEGIN{var=123; print "var = " var}'
  2. var = 123

  3. awk 'BEGIN{var=123;printf "%s %5f\n", "var =",var}'
  4. var = 123.000000

  5. awk 'BEGIN{var=123;sprintf ("%s  %5f\n", "var =",var)}'


  6. awk ‘BEGIN{var1=123;var2=sprintf ("%5f",var1); print "var2 =" var2}’
  7. var2 = 123.000000
复制代码


9. “a==b?c:d” ?

这个是一个if语句的简写,即conditional expression1 ? expression2: expression3;完整写法为:

if(a==b) {c} else {d}


10. awk ‘! a[$0]++’ 怎么理解?

这是一个非常经典的去重复项的awk语句,虽然短小,不过涉及到了不少知识点,下面一一解读:

<1> :”!” 即非。

<2>:a[$0],以$0为数据下标,建立数组a

<3>:a[$0]++,即给数组a赋值,a[$0]+=1

<4> :那么组合起来,awk是怎么执行!a[$0]++的呢?我用一个实际例子来解释:



  1. cat file
  2. 111
  3. 222
  4. 111
  5. 222
  6. 333

  7. awk '{print a[$0],!a[$0]++,a[$0],!a[$0],$0}' file
  8.   1 1 0 111
  9.   1 1 0 222
  10. 1 0 2 0 111
  11. 1 0 2 0 222
  12.   1 1 0 333
复制代码

      https://www.gnu.org/software/gaw ... .html#Increment-Ops
    lvalue++
Increment lvalue, returning the old value of lvalue as the value of the expression.

awk ‘++a[$0]==1’ 和上面的代码作用一样,你理解了么?


11. 如何打印单双引号?



  1. awk 'BEGIN {print "single quote --> '\''";print "double quote --> \"" }'
  2. single quote --> '
  3. double quote --> "
复制代码



更可靠的的方法如Tim所示:

  1. awk 'BEGIN {print "single quote --> \047";print "double quote --> \042" }'
复制代码

12. awk 语句中多个{}是怎么执行的?

还是用个例子来说明:



  1. cat file
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5

  7. awk '$1==3{printf "|| "$0}{printf " @@ "$0}{print $0}' file # 这个语句中包含三个action
  8. @@ 11   # 判断$1==3?否;执行 action {printf " @@ "$0};执行 action {print $0}
  9. @@ 22   # 判断$1==3?否;执行 action {printf " @@ "$0};执行 action {print $0}
  10. || 3 @@ 33  # 判断$1==3?是,执行{print “|| “$0}; 执行 action {printf " @@ "$0};执行 action {print $0}
  11. @@ 44  # 判断$1==3?否;执行 action {printf " @@ "$0};执行 action {print $0}
  12. @@ 55  # 判断$1==3?否;执行 action {printf " @@ "$0};执行 action {print $0}
复制代码

这样可以清楚的看出,awk是一行一行读取文本,然后按照代码的前后顺序执行。但如果action中包含next或exit时,有所不同:



  1. awk '$1==3{printf "|| "$0;next}{printf "@@ "$0}{print $0}' file
  2. @@ 11
  3. @@ 22
  4. || 3@@ 44
  5. @@ 55

  6. awk '$1==3{printf "|| "$0;exit}{printf "@@ "$0}{print $0}' file
  7. @@ 11
  8. @@ 22
  9. || 3
复制代码


13. FS, OFS, RS, ORS?

最后用图解的方式说明一下这四个变量:

Picture2.png

评分

参与人数 9可用积分 +26 信誉积分 +50 收起 理由
方兆国儿 + 10 很给力!
_寒_CU + 10 SS博 ,很给力! 赞一个!
请叫我俊哥哈 + 10 好东西
substr函数 + 10 很给力!
zsszss0000 + 10 赞一个!
xiaopan3322 + 5 很不错,学习……
expert1 + 10 a
ywlscpl + 6 总结的很不错
ly5066113 + 5 善于总结是一个好习惯,再接再厉!

查看全部评分

论坛徽章:
2
射手座
日期:2014-10-10 15:59:4715-16赛季CBA联赛之上海
日期:2016-03-03 10:27:14
发表于 2011-04-26 08:41 |显示全部楼层
多谢Tim哥支持

论坛徽章:
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-26 08:44 |显示全部楼层
有几个地方感觉不是很准确。

6. Awk 语句中可以使用{n,m}这样的正则么?
可以,不过是一种GNU的扩展,使用方法:gawk -- re-interval

{n,m}这样的正则在什么版本的awk里都可以使用,并不是GNU的扩展,只不过使用的方式有所不同。

7. BEGIN 和END 到底是怎么一回事?
有时,对于新手可能也会是个问题。简单说下:
BEGIN {action} : 读取文本之前进行的操作,所以不可以妄想在BEGIN的{action}中,对文本进行任何操作,或获取任何文本的信息。

这个说法太绝对了,在BEGIN中可以使用getline函数来操作文本。

11. 如何打印单双引号?

这个我更倾向与用八进制数来表示,利用shell引号的解析来做看起来很混乱,就像变量的引用一样
  1. awk 'BEGIN {print "single quote --> \047";print "double quote --> \042" }'
复制代码

论坛徽章:
0
发表于 2011-04-26 08:45 |显示全部楼层
支持

论坛徽章:
2
射手座
日期:2014-10-10 15:59:4715-16赛季CBA联赛之上海
日期:2016-03-03 10:27:14
发表于 2011-04-26 08:46 |显示全部楼层
有几个地方感觉不是很准确。


{n,m}这样的正则在什么版本的awk里都可以使用,并不是GNU的扩展,只不过使 ...
ly5066113 发表于 2011-04-26 08:44



    好的,马上改正

论坛徽章:
0
发表于 2011-04-26 09:03 |显示全部楼层
好筒子

论坛徽章:
5
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:50:282015年亚洲杯之朝鲜
日期:2015-03-13 22:47:33IT运维版块每日发帖之星
日期:2016-01-09 06:20:00IT运维版块每周发帖之星
日期:2016-03-07 16:27:44
发表于 2011-04-26 09:09 |显示全部楼层
这种分享的精神绝对要支持!

有些地方可能值得商榷,如:

10.
原来,第一个a[$0]的值为空,由于“!”的运算级别高于“+”

++与+是不同的操作符,还有++的优先级比!高。

13. 里面的图示是不是弄反了?

论坛徽章:
2
射手座
日期:2014-10-10 15:59:4715-16赛季CBA联赛之上海
日期:2016-03-03 10:27:14
发表于 2011-04-26 09:26 |显示全部楼层
回复 7# blackold


   图已更正,黑哥真是火眼

   ++比!级别高的话,!a[$0]++是先执行a[$0]++么?那!a[$0]++不应该是0么?有点晕,黑哥指点下

论坛徽章:
5
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:50:282015年亚洲杯之朝鲜
日期:2015-03-13 22:47:33IT运维版块每日发帖之星
日期:2016-01-09 06:20:00IT运维版块每周发帖之星
日期:2016-03-07 16:27:44
发表于 2011-04-26 09:38 |显示全部楼层
回复 8# yinyuemi


    这是前加/后加的区别,前几天还说过。

   后加: 先使用变量的值,再自加。

   !a[$0]++ 对这个表达式的求值,它的值与 !a[$0] 相同(先使用变量a[$0]的值),但对表达式求值后 a[$0]会自加。


   图里面的 RS值前后不一致啊。

论坛徽章:
2
射手座
日期:2014-10-10 15:59:4715-16赛季CBA联赛之上海
日期:2016-03-03 10:27:14
发表于 2011-04-26 09:47 |显示全部楼层
回复  yinyuemi


    这是前加/后加的区别,前几天还说过。

   后加: 先使用变量的值,再自加。
  ...
blackold 发表于 2011-04-26 09:38



    我知道问题出哪里了,我之前“困”在那个运算级别的判断了,现在弄明白了,多谢黑哥
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP