AWK介绍
9.1    调用awk
三种方式:
1.    命令行方式:    
awk [-F filed-separator] 'commands' input-files
-F    指定分隔符(默认是空格或tab)
commands    awk的命令
input-files    要处理的文件
2.    将所有awk命令插入一个文件,并使awk程序可执行,然后用awk命令解释器作为脚本的首行,以便通过键入脚本名称来调用它
3.    将所有的awk命令插入一个单独文件,然后调用:
awk -f awk-script-file input-files
-f    指明调用脚本名
input-files    待处理的文件

9.2    awk脚本
在命令中调用awk时,awk脚本由各种操作和模式组成。
awk每次读一条记录或一行,并使用指定的分隔符分隔指定域。当新行出现时,awk命令获悉已读完整条记录,然后在下一个记录启动读命令,这个读进程将持续到文件尾或文件不再存在。
9.2.1    模式和动作
任何awk语句都由模式和动作组成。模式部分决定动作语句何时触发及触发条件。处理即对数据进行的操作。如果省略模式部分,动作将时刻保持执行状态。
模式可以是任何条件语句、复合语句或正则表达式,包括两个特殊字段BEGIN和END。使用BEGIN语句设置计数和打印头。BEGIN语句用在任何文本浏览动作之前,之后文本浏览动作依据输入文件开始执行。END语句用在awk完成文本浏览动作后打印输出文本总数和结尾状态标志。如果不特别指明模式,awk总是匹配。
实际动作在大括号{}内指明。动作大多数用来打印,但还有些更长的代码诸如if和循环语句及循环退出结构。如果不指明采取动作,awk将打印出所有浏览出来的记录。
9.2.2    域和记录
awk执行时,其浏览域标记为$1,$2…$n,这种方法称为域标识。当需要指定多域时使用逗号,用于分隔,如$1,$3指定第一域和第三域,如果希望指定所有域可使用$0。
使用print命令执行打印操作,这是一个awk命令,需要用{}括起来。
1.    抽取域
举例: 现有一个grade.txt文件,内容如下:
$ cat grade.txt
M.Tansley        05/99    48311    Green    8    40    44
J.Lulu            06/99    48317    Green    9    24    26
P.Bunny        02/99    48        Yellow    12    35    28
J.Troll            07/99    4842    Brown-3    12    26    26
L.Tansley        05/99    4712    Brwon-2    12    30    28
此文本文件有7个域,以空格作为域分隔符
2.    保存awk输出
两种方式保存awk的输出结果:
重定向到文件:(屏幕无显示)
$ awk '{print $0}' grade.txt >wow
使用管道将输出结果传给tee:(屏幕显示结果)
$ awk '{print $0}' grade.txt|tee woww
3.    使用标准输入
几种方法:
$ belts.awk grade.txt
$ belts.awk < grade.txt
4.    打印所有记录
$ awk '{print $0}' grade.txt
5.    打印单独记录
只打印$1和$4:
$ awk '{print $1,$4}' grade.txt
6.    自定义格式打印
可以在$1和$4的上方添加注释,Name和Belt:
$ awk 'BEGIN {print "Name\t\tBelt\n-------------------------"} {print $1"\t"$4}' grade.txt
在结尾加上结束注释:
$ awk 'BEGIN {print "Name\t\tBelt\n-------------------------"} {print $1"\t"$4} END {print "-------------------------\nEnd of report"}' grade.txt
7.    awk错误信息提示
在碰到awk错误时,可相应查找:
•    确保整个awk命令用单引号括起来
•    确保命令内所有引号成对出现
•    确保用大括号{}括起动作语句,用括号()括起条件语句
•    可能忘记使用大括号{}
8.    awk键盘输入
如果没有给出输入文件,awk从键盘获得输入,完成后按<Ctrl-D>结束输入
9.2.3    元字符
\ ^ $ . [ ] | ( ) * + ?
其中+和?只适用于awk而不适用于grep或sed:
+    匹配一个或一个以上前面的单字符
?    匹配0个或一个前面的单字符
举例:
/XY+Z/    匹配XYZ、XYYYYZ
/XY?Z/    匹配XYZ、XZ
9.2.4    条件操作符
操作符            描述
  <                小于
  <=              小于等于
  ==                等于
  !=               不等于
  >                大于
  >=              大于等于
  ~           匹配正则表达式
  !~          不匹配正则表达式
1.    匹配
使用~紧跟正则表达式可以匹配域,也可以用if,条件需要用()括起来
举例:
查询棕带学员的所有情况(打印出$4匹配Brown的行)
$ awk '{if ($4~/Brown/) print $0}' grade.txt

$ awk '$0 ~ /Brown/' grade.txt
2.    精确匹配
使用==并用双引号括起条件可以精确匹配条件
举例:
$ awk '{if ($3 == "48") print $0}' grade.txt
3.    不匹配
使用!~紧跟正则表达式不匹配域
举例:
查询不是棕带的所有学员情况(打印出$4不匹配Brown的行)
$ awk '{if ($4!~/Brown/) print $0}' grade.txt

$ awk '$0 !~ /Brown/' grade.txt
4.    比较
由于几个比较大小符的使用方法都一样,就随便举一个例子:
$ awk '{if ($6 < $7) print $1" try better at the next comp"}' grade.txt
5.    各种匹配
匹配Green或green:
$ awk '/[Gg]reen/' grade.txt
匹配$1的第四个字符是a:
$ awk '$1 ~ /^...a/' grade.txt
匹配Yellow或Brown:
$ awk '$4 ~ /Yellow|Brown/' grade.txt
匹配以J开头的行:
$ awk '$0 ~ /^J/' grade.txt
6.    复合表达式
复合模式或复合操作符用于形成复杂的逻辑操作,复合表达式即为模式间通过使用复合操作符互相结合起来的表达式
&&    AND:    符号两边必须同时为真
|| OR:    符号两边至少一边为真
! :    非 求逆
举例:
$ awk '{if ($1 == "P.Bunny" && $4 == "Yellow") print $0}' grade.txt
$ awk '{if ($4 == "Yellow" || $4 ~ /Brown/) print $0}' grade.txt
9.2.5    awk内置变量
awk有许多内置变量用来设置环境信息,常用的有:
ARGC        命令行参数个数
ARGV        命令行参数排列
ENVIRON        支持队列中系统环境变量的使用
FILENAME    awk读取的文件名
FNR            当前文件已处理的记录行数
FS            设置域分隔符,相当于-F
NF            域的列数
NR            已处理的记录总行数
OFS            设置输出字段域分隔符
RS/ORS        换行符

ARGC支持命令行中传入awk脚本的参数个数。ARGV是ARGC的参数排列数组,其中每一元素表示为ARGV[n],n为希望访问的命令行参数
ENVIRON支持系统设置的环境变量,要访问单独变量,使用实际变量名,如ENVIRON[“EDITOR”]=”Vi”
FILENAME为awk脚本当前操作文件的文件名
FNR为awk当前操作文件的记录行数,其值总是小于等于NR。如果脚本正在访问多个文件,每一新输入文件都将重置此变量
FS用来设置域分隔符,与命令行中-F相同,缺省为空格。如: 以冒号为分隔符FS=":"
NF为记录域个数(列数),在记录被读之后设置,常用$NF指定最后一列
OFS指定输出记录的域分隔符,缺省为空格。如: 以-为分隔符输出FS="-"
ORS与RS都为输出记录分隔符,缺省是新行(\n)
举例:
查看文件的记录数:
$ awk 'END {print NR}' grade.txt
打印所有记录及记录的行号及总域数,并在最后打印出读取文件的文件名:
$ awk '{print NF,NR,$0} END {print FILENAME}' grade.txt
至少包含一个记录且包含Brown:
$ awk '{if (NR > 0 && $4 ~ /Brown/) print $0}' grade.txt
NF的一个强大功能是将变量$PWD的返回值传入awk并显示其目录:
$ echo $PWD | awk -F/ '{print $NF}'
显示文件名:
$ echo "/usr/apache/conf/httpd.conf" | awk -F/ '{print $NF}'
9.2.6    awk操作符
awk操作符的基本表达式可以划分为数字型、字符串型、变量型域及数组元素
= += -= *= /= %= ^=            赋值操作符
?                        条件表达操作符
|| && !                    并、与、非
~ !~                        匹配操作符(匹配与不匹配)
< <= == != > >=                关系操作符
+ - * / % ^                算术操作符
++ --                        前缀和后缀
1.    设置输入域到域变量名
在awk中,设置有意义的域名是一种好习惯,在进行模式匹配或关系操作时更容易理解。一般的变量名设置方式为name=$n,这里name为调用的域变量名,n为实际域号。多个域名设置之间用分号;分隔(请注意下面;的使用)
$ awk '{name = $1;belts = $4; if (belts ~ /Yellow/) print name" is belt "belts}' grade.txt
2.    域值比较操作
有两种方式测试一数值域是否小于另一数值域:
1)    在BEGIN中给变量名赋值
2)    在关系操作符中使用实际数值
通常在BEGIN部分赋值是很有益的,可以在awk表达式进行改动时减少很多麻烦。使用关系操作时必须用圆括号括起来。举例:
查询所有比赛中得分在27点一下的学生:
$ awk '{if ($6 < 27) print $0}' grade.txt

$ awk 'BEGIN {BASELINE = "27"} {if ($6 < BASELINE) print $0}' grade.txt
3.    修改数值域取值
当在awk中修改任何域时,重要的一点是要记住实际输入文件是不可修改的,修改的只是保存在缓存里的awk复本。awk会在变量NR或NF中反映出修改痕迹
为修改数值域,简单的给域标识重赋新值。举例:
$ awk '{if ($1 == "M.Tansley") $6 = $6 - 1; print $1,$6,$7}' grade.txt
4.    修改文本域
修改文本域即对其重新赋值,赋给一个新的字符串。记住,字符串要使用双引号,并用圆括号括起整个语法。举例:
$ awk '{if ($1 == "J.Troll") ($1 = "J.L.Troll"); print $1}' grade.txt
5.    只显示修改记录
如果文件很大,修改的记录很多,所以需要只查看修改部分。在模式后面使用大括号{}将只打印修改部分。取得模式,再根据模式结果实施操作。举例:(注意大括号的位置)
$ awk '{if ($1 == "J.Troll") {$1 = "J.L.Troll"; print $1}}' grade.txt
6.    创建新的输出域
在awk中处理数据时,基于各域进行计算时创建新域是一种好习惯。创建新域要通过其他域赋予新域标识符。举例:
在grade.txt中创建新域8保存域目前级别分与域最高级别分的差值:
$ awk 'BEGIN {print "Name\tDifference"} {if ($6 < $7) {$8 = $7 - $6; print $1,$8}}' grade.txt
或者赋予便于理解的变量名:
$ awk 'BEGIN {print "Name\tDifference"} {if ($6 < $7) {diff = $7 - $6; print $1,diff}}' grade.txt
7.    增加列值
使用符号+=增加列数或进行运行结果统计。将符号右边的变量域的值赋给左边。
举例:
打印出所有学生的总成绩(将整个文件的$6求和,total为变量名):
$ awk '(total += $6); END {print "Club student total points: "total}' grade.txt
如果文件很大,只想打印结果部分而不是所有记录,在语句的外面加上大括号{}即可:
$ awk '{(total += $6)}; END {print "Club student total points: "total}' grade.txt
查看当前目录所有文件大小及总和(除去子目录):
$ ls -l|awk '/^[^d]/ {print $9"\t"$5} {total += $5} END {print "total Bytes: "total}'