[SHELL]awk的用法举例
从初学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.
- 9.awk '{print "NR = " NR " FNR = " FNR, $0}' file
- 10.NR = 1 FNR = 1 a
- 11.NR = 2 FNR = 2 b
- 12.NR = 3 FNR = 3 c
- 13.NR = 4 FNR = 4 d
- 14.NR = 5 FNR = 5 e
- 15.NR = 6 FNR = 6 f
- 16.
- 17.awk '{print "NR = " NR " FNR = " FNR, $0}' file file
- 18.NR = 1 FNR = 1 a
- 19.NR = 2 FNR = 2 b
- 20.NR = 3 FNR = 3 c
- 21.NR = 4 FNR = 4 d
- 22.NR = 5 FNR = 5 e
- 23.NR = 6 FNR = 6 f
- 24.NR = 7 FNR = 1 a
- 25.NR = 8 FNR = 2 b
- 26.NR = 9 FNR = 3 c
- 27.NR = 10 FNR = 4 d
- 28.NR = 11 FNR = 5 e
- 29.NR = 12 FNR = 6 f
3. Awk怎么引入变量?
有两种方法:
(1): awk -v var=$VAR '{code}'
(2): awk '{CODE}'$VAR'{CODE}'
例如:
点击(此处)折叠或打开
- 1.VAR=XXX
- 2.
- 3.awk -v var=$VAR 'BEGIN{print var}'
- 4.XXX
- 5.
- 6.awk 'BEGIN{print "'$VAR'"}'
- 7.XXX
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
点击(此处)折叠或打开
- 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/viewthr ... p;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.
- 8.awk 'BEGIN{while (getline <"file") print}' file
- 9.1
- 10.2
- 11.3
- 12.4
- 13.5
END {action}:
它在整个输入文件处理完成后被执行,同样无法对文本进行任何操作,如匹配某个pattern执行action。
8. print,printf 和sprintf?
print:为一般的打印
printf:可以定义打印格式
sprintf:可以完成和printf相同的功能,不同的是sprintf只能输出值,并不能完成打印的功能。
print默认有个换行\n,而printf没有,当然它和C语言的printf类似(awk本是c的近亲),能打印各种格式,但默认没有换行。
例如:
点击(此处)折叠或打开
- 1.awk 'BEGIN{var=123; print "var = " var}'
- 2.var = 123
- 3.
- 4.awk 'BEGIN{var=123;printf "%s %5f\n", "var =",var}'
- 5.var = 123.000000
- 6.
- 7.awk 'BEGIN{var=123;sprintf ("%s %5f\n", "var =",var)}'
- 8.
- 9.
- 10.awk ‘BEGIN{var1=123;var2=sprintf ("%5f",var1); print "var2 =" var2}’
- 11.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.
- 8.awk '{print a[$0],!a[$0]++,a[$0],!a[$0],$0}' file
- 9. 1 1 0 111
- 10. 1 1 0 222
- 11.1 0 2 0 111
- 12.1 0 2 0 222
- 13. 1 1 0 333
原来,第一个a[$0]的值为空,所以!a[$0]++是先作判断,结果为1(非空为真,即为1),再作数组赋值a[$0]++。这也就是为什么前面的!a[$0]++并不一定等于后面的!a[$0]。
awk ‘++a[$0]==1’ 和上面的代码作用一样,你理解了么?
11. 如何打印单双引号?
点击(此处)折叠或打开
- 1.awk 'BEGIN {print "single quote --> '\''";print "double quote --> \"" }'
- 2.single quote --> '
- 3.double quote --> "
点击(此处)折叠或打开
- 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.
- 8.awk '$1==3{printf "|| "$0}{printf " @@ "$0}{print $0}' file # 这个语句中包含三个action
- 9.@@ 11 # 判断$1==3?否;执行 action {printf " @@ "$0};执行 action {print $0}
- 10.@@ 22 # 判断$1==3?否;执行 action {printf " @@ "$0};执行 action {print $0}
- 11.|| 3 @@ 33 # 判断$1==3?是,执行{print “|| “$0}; 执行 action {printf " @@ "$0};执行 action {print $0}
- 12.@@ 44 # 判断$1==3?否;执行 action {printf " @@ "$0};执行 action {print $0}
- 13.@@ 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.
- 7.awk '$1==3{printf "|| "$0;exit}{printf "@@ "$0}{print $0}' file
- 8.@@ 11
- 9.@@ 22
- 10.|| 3
13. FS, OFS, RS, ORS?
最后用图解的方式说明一下这四个变量:
14.Awk 内置的字符串函数
gsub(r,s) |
在整个$0中用s代替r |
gsub(r,s,t) |
在整个t中用s替代r |
index(s,t) |
返回s中字符串t的第一位置 |
length(s) |
返回s长度 |
match(s,r) |
测试s是否包含匹配r的字符串 |
split(s,a,fs) |
在fs上将s分成序列a |
sprint(fmt,exp) |
返回经fmt格式化后的exp |
sub(r,s) |
用$0中最左边最长的子串代替s |
substr(s,p) |
返回字符串s中从p开始的后缀部分 |
substr(s,p,n) |
返回字符串s中从p开始长度为n的后缀部分 |
gsub函数有点类似于sed查找和替换。它允许替换一个字符串或字符为另一个字符串或字符,并以正则表达式的形式执行。第一个函数作用于记录$0,第二个gsub函数允许指定目标,然而,如果未指定目标,缺省为$0。
index(s,t)函数返回目标字符串s中查询字符串t的首位置。length函数返回字符串s字符长度。
match函数测试字符串s是否包含一个正则表达式r定义的匹配。
split使用域分隔符fs将字符串s划分为指定序列a。
sprint函数类似于printf函数,返回基本输出格式fmt的结果字符串exp。
sub(r,s)函数将用s替代$0中最左边最长的子串,该子串被r匹配。
substr(s,p)返回字符串s在位置p后的后缀。
substr(s,p,n)同上,并指定子串长度为n。
此外,$0变量是指整条记录。$1表示当前行的第一个域,$2表示当前行的第二个域,......以此类推。
awk中的gsub的&,表示要被替换的原始字符。gsub(/[0-9]+/,”\\0x&”),这时&表示[0-9]所匹配到的字符。
15.awk常见错误检查
当第一次使用awk时,可能被错误信息搅得不知所措,但通过长时间和不断的学习,可总
结出以下规则。在碰到 awk错误时,可相应查找:
? 确保整个awk命令用单引号括起来。
? 确保命令内所有引号成对出现。
? 确保用花括号括起动作语句,用圆括号括起条件语句。
? 可能忘记使用花括号,也许你认为没有必要,但 awk不这样认为,将按之解释语法。
?正则表达式用斜线括起来! \ ^ $ . [] | () * + ?
ARGC:支持命令行中传入 a w k脚本的参数个数。
ARGV:是ARGC的参数排列数组,其中每一元素表示为ARGV[n], n为期望访问的命令行参数。
ENVIRON :支持系统设置的环境变量,要访问单独变量,使用实际变量名,例如
ENVIRON [“ EDITOR”] =“Vi”。
FILENAME:支持awk脚本实际操作的输入文件。
因为awk可以同时处理许多文件,因此如果访问了这个变量,将告之系统目前正在浏览的实际文件。
FNR:支持awk目前操作的记录数。其变量值小于等于 NR。如果脚本正在访问许多文件,
每一新输入文件都将重新设置此变量。
FS:用来在awk中设置域分隔符,与命令行中 - F选项功能相同。缺省情况下为空格。
如果用逗号来作域分隔符,设置 FS = ","。
NF:支持记录域个数,在记录被读之后再设置。
OFS:允许指定输出域分隔符,缺省为空格。如果想设置为 #,写入OFS = "#"。
ORS:为输出记录分隔符,缺省为新行(\n)。
RS:是记录分隔符,缺省为新行 (\n)。
16.awk操作符
域值比较操作
有两种方式测试一数值域是否小于另一数值域。
1) 在BEGIN中给变量名赋值。
2) 在关系操作中使用实际数值。
通常在BEGIN部分赋值是很有益的,可以在awk表达式进行改动时减少很多麻烦。
使用关系操作必须用圆括号括起来。
AWK内置字符串函数
从shell中向awk传入字符串
(1) 使用管道将字符串stand-by传入awk,返回其长度。
(2) 设置文件名为一变量,管道输出到awk,返回不带扩展名的文件名。
(3) 设置文件名为一变量,管道输出到awk,只返回其扩展名。
字符串屏蔽序列
printf修饰符
向一行awk命令传值
在查看awk脚本前,先来查看怎样在 awk命令行中传递变量。
在awk执行前将值传入awk变量,需要将变量放在命令行中,格式如下:
awk 命令变量=输入文件值
在awk中使用F S变量
使用awk脚本时,记住设置 FS变量是在 BEGIN部分。
向awk脚本传值
格式:awk script_file var=value input_file
awk数组
数组使用前,不必定义,也不必指定数组元素个数。经常使用循环来访问数组。下面是
一种循环类型的基本结构:for (element in array ) print array[element]