AWK是一种强大的文本处理工具,其处理文本的效率极高,其输出可以来自标注输入、其他命令的输出或一个或多个文件,熟练使用awk将会对工作效率有很大的提升。
awk调用方式
awk调用方式包括三种:
一、命令行调用
1
| awk [-F seperator] 'commond' input-file
|
commond的组成又可以包括多个模式和动作的组合,即命令行调用又可以写为:
1
| akw [-F seperator] 'parrtern1 {Action1} pattern2 {Action2}' input-file
|
- seperator:分隔符。分隔符为可选参数,可以为任意字符串,若不指定,默认分隔符为空格。
- commond:awk命令
- input_file: 待处理的文本文件
二、脚本调用
1
| awk -f awk-script-file input-file
|
将awk命令写入一个文件中,然后使用-f参数指定该文件运行
三、shell脚本插入awk命令
在shell中插入awk命令对文件进行处理,直接执行shell命令。
模式与动作
任何awk语句都由**模式**和**动作**组成,**模式部分决定Action语句何时触发以及触发事件,动作决定对当前被匹配的数据进行的操作**,Action中由多个awk处理语句组成。
1
| awk [-F seperator] 'parrtern1 {Action1} pattern2 {Action2}' input-file
|
注意问题:
- awk语句必须被单引号或双引号包含,防止awk语句被当做shell命令解析。
- 确保awk命令中所有引号都成对出现
确保用花括号括起来动作语句,用圆括号括起来条件语句
模式与动作一一对应,只有pattern匹配成功,对应的action(用{}括起来表示一个action)才会执行。
模式可以为任何条件语句或复合语句或正则表达式,也可以为awk保留字BEGIN,END
模式尽量不要加双引号,否则某些情况下可能会失效,如”$1>30”。
action一定要用{}括起来,一个{}括起来的动作属于一个action,不同{}括起来的动作属于不同action,其对应不同的pattern。
BEGIN和END为特殊的模式,BEGIN模式使用在任何文本浏览之前,常用来做一些初始化设置或打印头部信息等,END模式使用在完成文本浏览动作之后,常用来处理一些收尾打印等工作。注意BEGIN和END语句都仅且执行一次
awk基本用法
假定一下为一个学校中抽样的几个学生的成绩(score.txt),下面四列分别为学号,名字,年级,分数:
1 2 3 4
| 13331264 tom grade4 94 13342010 marry grade4 90 13315012 jemmy grade1 85 13323089 jane grade2 80
|
域标识
awk从输入文件中**每次读取一行**,当遇到分割符时将其分割为域,这些域被标记为\$1,\$2,\$3...,直到读到文件结尾或文件不存在,$0表示当前记录(即当前行)。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| # 不加pattern awk '{print $1,$4}' score.txt 输出结果: 13331264 94 13342010 90 13315012 85 13323089 80 # pattern为$4>=90,过滤分数超过90分的用户才处理 awk '$4>=90 {print $1,$4}' score.txt 输出结果: 13331264 94 13342010 90 # 命令有两个parttern action组,第一个pattern为BEGIN,第二个patter为$4>=90 awk 'BEGIN {print "学号 成绩"} $4>=90 {print $1,$4}' score.txt 输出结果: 学号 成绩 13331264 94 13342010 90
|
条件控制语句
关系与正则运算符:
符号 |
描述 |
< |
小于 |
<= |
小于等于 |
> |
大于 |
>= |
大于等于 |
== |
等于 |
~ |
匹配正则表达式(二元 符号前面为被匹配的字符串,后面为模式串,一般模式传用/pattern/括起来) |
!~ |
不匹配正则表达式 |
逻辑运算符
逻辑运算符与C语言中完全一致。
符号 |
描述 |
&& |
且,两个条件同时满足才为真 |
\ |
\ |
|
或,只要有一个条件为真即为真 |
! |
将结果取反(一元运算符) |
关键字
关键字 |
描述 |
break |
用于while或for循环,退出循环 |
continue |
终止当前循环,执行下一个循环 |
next |
导致读入下一个输入行,并返回到脚本的顶部。这可以避免对当前输入行执行其他的操作过程。 |
exit |
退出执行,直接跳转到END执行,若没有END,终止脚本 |
if条件控制语句
在awk中使用if判断条件时,必须将if后面的条件用()括起来,与C语言类似。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| awk '{if($1~/1333/) print $0}' score.txt 输出: 13331264 tom grade4 94 awk '{if($1!~/1333/) print $0}' score.txt 输出: 13342010 marry grade4 90 13315012 jemmy grade1 85 13323089 jane grade2 80 awk '{if($4>85) print $0}' score.txt 输出: 13331264 tom grade4 94 13342010 marry grade4 90 awk '{if($4>90&&$4<100) print $0}' score.txt 输出: 13331264 tom grade4 94
|
当然我们使用模式代替if条件判断,这个可以达到相同的效果
1 2 3 4 5
| # 使用条件语句 awk '{if($4>85) print $0}' score.txt # 使用模式过滤行 awk '$4>85 {print $0}' score.txt
|
for循环控制语句
语法
1 2 3 4 5 6 7 8 9 10 11
| for (变量 in 数组) { do_something; } for (变量;条件;表达式) { do_something; }
|
使用示例
1 2 3 4 5
| # ENVIRON为awk内置的环境变量,下面会说到。其为一个数组,该作用为打印环境变量中的所有键值对 awk 'BEGIN {for(k in ENVIRON) {print k"="ENVIRON[k];}}' # 打印0-9 awk 'BEGIN {for(i=0;i<10;i++) {print i}}'
|
while循环控制语句
语法:
1 2 3 4
| while (条件表达式) { do_something; }
|
使用示例
1 2
| # 打印0-9 awk 'BEGIN {i=0; while (i<10){print i;i++}}'
|
do while循环控制语句
语法:
1 2 3 4 5
| do { do_something } while (条件表达式)
|
使用示例
1 2
| #打印0-9 awk 'BEGIN {i=0;do{print i; i++;} while (i<10)}'
|
awk运算
算术运算符
符号 |
描述 |
+ - * / % |
加/减/乘/除/取余 |
++ |
自增1 |
– |
自减1 |
+ |
一元加操作符,将操作数乘以1 |
- |
一元减操作符,将操作数乘以-1 |
^ |
求幂。如2^2=4 |
赋值运算符
符号 |
描述 |
= |
赋值 |
+=、-=、*=、/=、%=、^= |
将左右操作数进行对应操作,然后赋值给左操作数(与C语言完全一致) |
其他运算符
符号 |
描述 |
$ |
字段引用(引用域) |
? : |
条件表达式(与C语言一致) |
空格 |
字符串连接符 |
in |
判断数组中是否存在某个键值 |
运算符优先级:
awk数组
awk数组是一种关联数组,其下标既可以是数字,也可以是字符串。
- 无需定义,数组在使用时被定义
- 数组元素的初始值为0或者空字符串
- 数组可以自动扩展
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| awk 'BEGIN {a["123"]=2;print a["123"]}' 输出: 2 awk 'BEGIN {a[1]=2;a[2]=3;a[3]=4; for(k in a) print k"="a[k];}' 输出: 1=2 2=3 3=4 awk 'BEGIN {a[1]=2;a[2]=3;a[3]=4; print 5 in a; print 1 in a}' 输出: 0 1 awk 'BEGIN {a[1]=2;a[2]=3;a[3]=4; delete a[1];for(k in a) print k"="a[k];}' 输出: 2=3 3=4 awk 'BEGIN {a[1,2]=10; for(k in a) print k"="a[k];}' 输出: 12=10 awk 'BEGIN {SUBSEP=":";a[1,2]=10; for(k in a) print k"="a[k];}' 输出: 1:2=10
|
awk内置变量
变量 |
描述 |
ARGC |
命令行参数个数,awk后参数个数 |
ARGV |
命令行参数数组,数组下标从0开始 |
ENVIRON |
系统环境变量数组 |
FILENAME |
输入文件的名字 |
FNR |
浏览文件的记录数(文件中的记录数,若多个文件不会累加) |
NR |
已读记录数(已读的记录数,若多文件会离家) |
NF |
浏览记录域的个数(即每行分割的域的最大值) |
FS |
设置域分割符,常用于BEGIN中设置域分割符 |
RS |
设置记录分隔符,原记录分隔符默认为换行,即一行一行读取,可使用该参数控制其不按照行读取 |
OFS |
设置输出域分隔符,原域默认分隔符为空格,可使用此分隔符修改 |
ORS |
设置输出记录分隔符。原记录默认分隔符为换行,可使用此参数修改 |
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| awk 'BEGIN {print ARGC}' score.txt 输出: 2 awk 'BEGIN {print ARGC; print ARGV[0];print ARGV[1]}' score.txt 输出: 2 awk score.txt awk 'BEGIN {for(k in ENVIRON) {print k"="ENVIRON[k];}}' awk 'BEGIN {i=0} {if(i==0){print FILENAME;i++}}' score.txt 输出: score.txt awk ' END {print FNR}' score.txt 输出: 1 2 3 4 awk '{print NR}' score.txt 输出: 1 2 3 4 awk ' END {print NF}' score.txt awk 'BEGIN {FS="\t"} {print NR}' score.txt awk -F'\t' '{print NR}' score.txt awk 'BEGIN {OFS="|"} {print $1,$2}' score.txt 输出: 13331264|tom 13342010|marry 13315012|jemmy 13323089|jane awk 'BEGIN {ORS="|"} {print $1,$2}' score.txt 输出: 13331264 tom|13342010 marry|13315012 jemmy|13323089 jane| awk 'BEGIN {RS="1333"} {print $1,$2}' score.txt 输出: 1264 tom
|
FNR与NR区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| cat a.txt 111 222 111 333 444 cat b.txt 111 555 666 awk '{print FNR}' a.txt b.txt 1 2 3 4 5 1 2 3 awk '{print NR}' a.txt b.txt 1 2 3 4 5 6 7 8
|
awk内置函数
计算相关函数:
函数 |
描述 |
cos(expr) |
计算余弦值,参数为弧度 |
sin(expr) |
计算正弦值,参数为弧度 |
int(expr) |
取整 |
log(expr) |
计算expr的自然对数 |
sqrt(expr) |
计算expr的平方根 |
1 2 3 4 5 6 7 8 9 10 11 12 13
| awk 'BEGIN {print cos(60*3.1415936/180)}' 输出: 0.5 awk 'BEGIN {print int(20.5)}' 输出: 20 awk 'BEGIN {print log(10)}' 输出: 2.30259
|
注意:awk字符串下标从1开始不是从0开始
字符串相关函数:
函数 |
描述 |
sub(src,des) |
将0中src第一次出现的子串替换为des |
sub(src,des,str) |
将字符串str中第一次出现的src替换为des。 |
gsub(src,des) |
将0中的src全部替换为des,若0中包含src,则返回1否则返回0 |
gsub(src,des,str) |
将字符串str中的所有src替换为des, |
index(str,substr) |
返回str中字符串substr首次出现的位置,位置从1开始,若未找到则返回0 |
length(str) |
返回str的长度 |
match(str, substr) |
测试str中是否存在子串substr |
split(str,result,sep) |
将str以sep为分割符分割为数组,并存到result中 |
printf(format,…) |
格式化输出,与C语言类似 |
substr(str,start) |
返回从start开始一直到最后的子字符串,与C++类似 |
substr(str,start,n) |
返回从start开始长度为n的子字符串,与C++类似 |
常用printf format
format |
说明 |
%c |
ascii字符 |
%d |
整数 |
%e |
浮点数,科学计数法 |
%f |
浮点数(如1.234) |
%o |
八进制数 |
%x |
十六进制数 |
%s |
字符串 |
更多format详见(http://wiki.jikexueyuan.com/project/awk/pretty-printing.html)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| awk 'BEGIN {s="aaabbbaaaccc";sub("aaa","1",s);print s}' 输出: 1bbbaaaccc awk '{gsub("t","s");print $0}' ./score.txt 输出: 13331264 som grade4 94 13342010 marry grade4 90 13315012 jemmy grade1 85 13323089 jane grade2 80 awk '{gsub("133", "45",$1);print $0}' ./score.txt 输出: 4531264 tom grade4 94 4542010 marry grade4 90 4515012 jemmy grade1 85 4523089 jane grade2 80 awk '{r = index($2,"m");print r}' ./score.txt 输出: 3 1 3 0 awk '{print length($2)}' ./score.txt 输出: 3 5 5 4 awk '{print match($2,"to")}' ./score.txt 输出: 1 0 0 0 awk 'BEGIN {print split("this is a test",result, " "); for(k in result) {print k":"result[k]}}' awk 'BEGIN {s="aaabbbccc";print substr(s,2)}' 输出: aabbbccc
|
AWK几个例子
文件去重并统计相同记录出现次数(保留记录原来的顺序)
1 2 3 4 5 6 7 8 9 10 11
| test.txt 111 222 111 333 444 awk '!arr[$0]++' test.txt awk '{!arr[$0]++} END {for (k in arr) print k,arr[k]}' test.txt
|
文件内容合并
1 2 3 4 5 6
| test.txt.1 111 555 666 awk '{if(!arr[$0]) {print $0; arr[$0]++}}' test.txt test.txt.1
|
找出文件A中存在且文件B中不存在的记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| A: 111 222 333 444 B: 333 444 awk 'NR==FNR {a[$0]=1} NR>FNR {if (!a[$0]) print $0}' b.txt a.txt 输出: 111 222
|