linux awk命令详解
awk是行处理器:相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本信息
awk处理过程:依次对每一行进行处理,然后输出
awk命令形式:
awk[-F|-f|-v]‘BEGIN{}//{command1;command2}END{}’file
[-F|-f|-v]大参数,-F指定分隔符,-f调用脚本,-v定义变量var=value
''引用代码块
BEGIN初始化代码块,在对每一行进行处理之前,初始化代码,主要是引用全局变量,设置FS分隔符
//匹配代码块,可以是字符串或正则表达式
{}命令代码块,包含一条或多条命令
;多条命令使用分号分隔
END结尾代码块,在对每一行进行处理之后再执行的代码块,主要是进行最终计算或输出结尾摘要信息
特殊要点:
$0表示整个当前行
$1每行第一个字段
NF字段数量变量
NR每行的记录号,多文件记录递增
FNR与NR类似,不过多文件记录不递增,每个文件都从1开始
\t制表符
\n换行符
FSBEGIN时定义分隔符
RS输入的记录分隔符,默认为换行符(即文本是按一行一行输入)
~匹配,与==相比不是精确比较
!~不匹配,不精确比较
==等于,必须全部相等,精确比较
!=不等于,精确比较
&& 逻辑与
||逻辑或
+匹配时表示1个或1个以上
/[0-9][0-9]+/两个或两个以上数字
/[0-9][0-9]*/一个或一个以上数字
FILENAME文件名
OFS输出字段分隔符,默认也是空格,可以改为制表符等
ORS输出的记录分隔符,默认为换行符,即处理结果也是一行一行输出到屏幕
-F'[:#/]'定义三个分隔符
print&$0
print是awk打印指定内容的主要命令
awk'{print}'/etc/passwd==awk'{print$0}'/etc/passwd
awk'{print""}'/etc/passwd//不输出passwd的内容,而是输出相同个数的空行,进一步解释了awk是一行一行处理文本
awk'{print"a"}'/etc/passwd//输出相同个数的a行,一行只有一个a字母
awk-F":"'{print$1}'/etc/passwd
awk-F:'{print$1;print$2}'/etc/passwd//将每一行的前二个字段,分行输出,进一步理解一行一行处理文本
awk-F:'{print$1,$3,$6}'OFS="\t"/etc/passwd//输出字段1,3,6,以制表符作为分隔符
-f指定脚本文件
awk-fscript.awkfile
BEGIN{
FS=":"
}
{print$1}//效果与awk-F":"'{print$1}'相同,只是分隔符使用FS在代码自身中指定
awk'BEGIN{X=0}/^$/{X+=1}END{print"Ifind",X,"blanklines."}'test
Ifind4blanklines.
ls-l|awk'BEGIN{sum=0}!/^d/{sum+=$5}END{print"totalsizeis",sum}'//计算文件大小
totalsizeis17487
-F指定分隔符
$1指指定分隔符后,第一个字段,$3第三个字段,\t是制表符
一个或多个连续的空格或制表符看做一个定界符,即多个空格看做一个空格
awk-F":"'{print$1}'/etc/passwd
awk-F":"'{print$1$3}'/etc/passwd//$1与$3相连输出,不分隔
awk-F":"'{print$1,$3}'/etc/passwd//多了一个逗号,$1与$3使用空格分隔
awk-F":"'{print$1""$3}'/etc/passwd//$1与$3之间手动添加空格分隔
awk-F":"'{print"Username:"$1"\t\tUid:"$3}'/etc/passwd//自定义输出
awk-F:'{printNF}'/etc/passwd//显示每行有多少字段
awk-F:'{print$NF}'/etc/passwd//将每行第NF个字段的值打印出来
awk-F:'NF==4{print}'/etc/passwd//显示只有4个字段的行
awk-F:'NF>2{print$0}'/etc/passwd//显示每行字段数量大于2的行
awk'{printNR,$0}'/etc/passwd//输出每行的行号
awk-F:'{printNR,NF,$NF,"\t",$0}'/etc/passwd//依次打印行号,字段数,最后字段值,制表符,每行内容
awk-F:'NR==5{print}'/etc/passwd//显示第5行
awk-F:'NR==5||NR==6{print}'/etc/passwd//显示第5行和第6行
route-n|awk'NR!=1{print}'//不显示第一行
//匹配代码块
//纯字符匹配!//纯字符不匹配~//字段值匹配!~//字段值不匹配~/a1|a2/字段值匹配a1或a2
awk'/mysql/'/etc/passwd
awk'/mysql/{print}'/etc/passwd
awk'/mysql/{print$0}'/etc/passwd//三条指令结果一样
awk'!/mysql/{print$0}'/etc/passwd//输出不匹配mysql的行
awk'/mysql|mail/{print}'/etc/passwd
awk'!/mysql|mail/{print}'/etc/passwd
awk-F:'/mail/,/mysql/{print}'/etc/passwd//区间匹配
awk'/[2][7][7]*/{print$0}'/etc/passwd//匹配包含27为数字开头的行,如27,277,2777...
awk-F:'$1~/mail/{print$1}'/etc/passwd//$1匹配指定内容才显示
awk-F:'{if($1~/mail/)print$1}'/etc/passwd//与上面相同
awk-F:'$1!~/mail/{print$1}'/etc/passwd//不匹配
awk-F:'$1!~/mail|mysql/{print$1}'/etc/passwd
IF语句
必须用在{}中,且比较内容用()扩起来
awk-F:'{if($1~/mail/)print$1}'/etc/passwd//简写
awk-F:'{if($1~/mail/){print$1}}'/etc/passwd//全写
awk-F:'{if($1~/mail/){print$1}else{print$2}}'/etc/passwd//if...else...
条件表达式
==!=>>=
awk-F":"'$1=="mysql"{print$3}'/etc/passwd
awk-F":"'{if($1=="mysql")print$3}'/etc/passwd//与上面相同
awk-F":"'$1!="mysql"{print$3}'/etc/passwd//不等于
awk-F":"'$3>1000{print$3}'/etc/passwd//大于
awk-F":"'$3>=100{print$3}'/etc/passwd//大于等于
awk-F":"'$3<1{print$3}'/etc/passwd//小于
awk-F":"'$3<=1{print$3}'/etc/passwd//小于等于
逻辑运算符
&& ||
awk-F:'$1~/mail/&&$3>8{print}'/etc/passwd//逻辑与,$1匹配mail,并且$3>8
awk-F:'{if($1~/mail/&&$3>8)print}'/etc/passwd
awk-F:'$1~/mail/||$3>1000{print}'/etc/passwd//逻辑或
awk-F:'{if($1~/mail/||$3>1000)print}'/etc/passwd
数值运算
awk-F:'$3>100'/etc/passwd
awk-F:'$3>100||$3<5'/etc/passwd
awk-F:'$3+$4>200'/etc/passwd
awk-F:'/mysql|mail/{print$3+10}'/etc/passwd//第三个字段加10打印
awk-F:'/mysql/{print$3-$4}'/etc/passwd//减法
awk-F:'/mysql/{print$3*$4}'/etc/passwd//求乘积
awk'/MemFree/{print$2/1024}'/proc/meminfo//除法
awk'/MemFree/{printint($2/1024)}'/proc/meminfo//取整
输出分隔符OFS
awk'$6~/FIN/||NR==1{printNR,$4,$5,$6}'OFS="\t"netstat.txt
awk'$6~/WAIT/||NR==1{printNR,$4,$5,$6}'OFS="\t"netstat.txt
//输出字段6匹配WAIT的行,其中输出每行行号,字段4,5,6,并使用制表符分割字段
输出处理结果到文件
①在命令代码块中直接输出route-n|awk'NR!=1{print>"./fs"}'
②使用重定向进行输出route-n|awk'NR!=1{print}'>./fs
格式化输出
netstat-anp|awk'{printf"%-8s%-8s%-10s\n",$1,$2,$3}'
printf表示格式输出
%格式化输出分隔符
-8长度为8个字符
s表示字符串类型
打印每行前三个字段,指定第一个字段输出字符串类型(长度为8),第二个字段输出字符串类型(长度为8),
第三个字段输出字符串类型(长度为10)
netstat-anp|awk'$6=="LISTEN"||NR==1{printf"%-10s%-10s%-10s\n",$1,$2,$3}'
netstat-anp|awk'$6=="LISTEN"||NR==1{printf"%-3s%-10s%-10s%-10s\n",NR,$1,$2,$3}'
IF语句
awk-F:'{if($3>100)print"large";elseprint"small"}'/etc/passwd
small
small
small
large
small
small
awk-F:'BEGIN{A=0;B=0}{if($3>100){A++;print"large"}else{B++;print"small"}}END{printA,"\t",B}'/etc/passwd
//ID大于100,A加1,否则B加1
awk-F:'{if($3<100)next;elseprint}'/etc/passwd//小于100跳过,否则显示
awk-F:'BEGIN{i=1}{if(i
awk-F:'BEGIN{i=1}{if(i
另一种形式
awk-F:'{print($3>100?"yes":"no")}'/etc/passwd
awk-F:'{print($3>100?$3":\tyes":$3":\tno")}'/etc/passwd
while语句
awk-F:'BEGIN{i=1}{while(i
7root1
7x2
703
704
7root5
7/root6
数组
netstat-anp|awk'NR!=1{a[$6]++}END{for(iina)printi,"\t",a[i]}'
netstat-anp|awk'NR!=1{a[$6]++}END{for(iina)printf"%-20s%-10s%-5s\n",i,"\t",a[i]}'
95231
99291
LISTEN6
79031
3038/cupsd1
79131
108371
98331
应用1
awk-F:'{printNF}'helloworld.sh//输出文件每行有多少字段
awk-F:'{print$1,$2,$3,$4,$5}'helloworld.sh//输出前5个字段
awk-F:'{print$1,$2,$3,$4,$5}'OFS='\t'helloworld.sh//输出前5个字段并使用制表符分隔输出
awk-F:'{printNR,$1,$2,$3,$4,$5}'OFS='\t'helloworld.sh//制表符分隔输出前5个字段,并打印行号
应用2
awk-F'[:#]''{printNF}'helloworld.sh//指定多个分隔符:#,输出每行多少字段
awk-F'[:#]''{print$1,$2,$3,$4,$5,$6,$7}'OFS='\t'helloworld.sh//制表符分隔输出多字段
应用3
awk-F'[:#/]''{printNF}'helloworld.sh//指定三个分隔符,并输出每行字段数
awk-F'[:#/]''{print$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12}'helloworld.sh//制表符分隔输出多字段
应用4
计算/home目录下,普通文件的大小,使用KB作为单位
ls-l|awk'BEGIN{sum=0}!/^d/{sum+=$5}END{print"totalsizeis:",sum/1024,"KB"}'
ls-l|awk'BEGIN{sum=0}!/^d/{sum+=$5}END{print"totalsizeis:",int(sum/1024),"KB"}'//int是取整的意思
应用5
统计netstat-anp状态为LISTEN和CONNECT的连接数量分别是多少
netstat-anp|awk'$6~/LISTEN|CONNECTED/{sum[$6]++}END{for(iinsum)
printf"%-10s%-6s%-3s\n",i,"",sum[i]}'
应用6
统计/home目录下不同用户的普通文件的总数是多少?
ls-l|awk'NR!=1&&!/^d/{sum[$3]++}END{for(iinsum)
printf"%-6s%-5s%-3s\n",i,"",sum[i]}'
mysql199
root374
统计/home目录下不同用户的普通文件的大小总size是多少?
ls-l|awk'NR!=1&&!/^d/{sum[$3]+=$5}END{for(iinsum)
printf"%-6s%-5s%-3s%-2s\n",i,"",sum[i]/1024/1024,"MB"}'
应用7
输出成绩表
awk'BEGIN{math=0;eng=0;com=0;printf"Lineno.NameNo.MathEnglishComputerTotal\n";printf"------------------------------------------------------------\n"}{math+=$3;eng+=$4;com+=$5;printf"%-8s%-7s%-7s%-7s%-9s%-10s%-7s\n",NR,$1,$2,$3,$4,$5,$3+$4+$5}END{printf"------------------------------------------------------------\n";printf"%-24s%-7s%-9s%-20s\n","Total:",math,eng,com;printf"%-24s%-7s%-9s%-20s\n","Avg:",math/NR,eng/NR,com/NR}'test0
[root@localhosthome]#cattest0
Marry2143788477
Jack2321667845
Tom2122487771
Mike2537879795
Bob2415405762
1、查询整张表记录,where条件过滤,关键词:where
select*fromuser;awk1user;
select*fromconsumerwherecost>100;
awk'$2>100'consumer
2、对某个字段去重,或者按记录去重,关键词:distinct
selectdistinct(date)fromconsumer;
awk'!a[$3]++{print$3}'consumer
selectdistinct(*)fromconsumer;
awk'!a[$0]++'consumer
3、记录按序输出,关键词:orderby
selectidfromuserorderbyid;
awk'{a[$1]}END{asorti(a);for(i=1;i<=length(a);i++){printa[i]}}'user
4、取前多少条记录,关键词:limit
select*fromconsumerlimit2;
awk'NR<=2'consumer
awk'NR>2{exit}1'consumer#performanceisbetter
5、分组求和统计,关键词:groupby、having、sum、count
selectid,count(1),sum(cost)fromconsumergroupbyidhavingcount(1)>2;
awk'{a[$1]=a[$1]==""?$2:a[$1]","$2}END{for(iina){c=split(a[i],b,",");if(c>2){sum=0;for(jinb){sum+=b[j]};printi"\t"c"\t"sum}}}'consumer
6、模糊查询,关键词:like(like属于通配,也可正则REGEXP)
selectnamefromuserwherenamelike'wang%';
awk'$2~/^wang/{print$2}'user
selectaddrfromuserwhereaddrlike'%bei';
awk'/.*bei$/{print$3}'user
selectaddrfromuserwhereaddrlike'%bei%';
awk'$3~/bei/{print$3}'user
7、多表join关联查询,关键词:join
selecta.*,b.*fromuserainnerjoinconsumerbona.id=b.idandb.id=2;
awk'ARGIND==1{a[$1]=$0;next}{if(($1ina)&&$1==2){printa[$1]"\t"$2"\t"$3}}'userconsumer
8、多表水平联接,关键词:unionall
selecta.*fromuseraunionallselectb.*fromuserb;
awk1useruser
selecta.*fromuseraunionselectb.*fromuserb;
awk'!a[$0]++'useruser
9、随机抽样统计,关键词:orderbyrand()
SELECT*FROMconsumerORDERBYRAND()LIMIT2;
awk'BEGIN{srand();while(i<2){k=int(rand()*10)+1;if(!(kina)){a[k];i++}}}(NRina)'consumer
1、统计07、08年每月交易发生笔数,按月排序
selectsubstr(date,1,6),count(*)frommytablewheredatebetween'20070101'and'20081231'groupbysubstr(date,1,6)orderbysubstr(date,1,6)
awk-F"|"'$1>=20070101&&$1<=20081231{a[substr($1,1,6)]++}END{for(iina)printi,a[i]}'file|sort-k1,1n
2、统计07、08年各类交易发生的笔数、金额
selectzy,count(*),sum(je)frommxwheredatebetween'20070101'and'20081231'groupbyzy
awk-F"|"'$1>=20070101&&$1<=20081231{a[$2]+=$4;b[$2]++}END{for(iina)printi,b[i],a[i]}'file
3、嗯,在我的存折明细中,按月统计下07、08年每个操作员、每月的交易发生笔数吧,扣电费、电话费(czy为auto)的不统计,结果按月份、操作员号排序
selectsubstr(date,1,6)\"月份\",czy,count(*)\"笔数\"frommytablewhereczy
<>'auto'anddatebetween'2007010'and'20081231'groupbysubstr(date,1,6),czy
orderbysubstr(date,1,6),czy
awk-F"|"'$6!="auto"&&substr($1,4,1)~/7|8/{a[substr($1,1,6)""$6]++}END{for(iina)printi,a[i]}
'file|sort-k1,1n-k2,2
4、又想到稍微复杂点的,用到了sql语句的having筛选。
统计每年发工资的总额,显示超过750元的年份。
selectsubstr(date,1,4),sum(je)frommytablewherezy='工资'groupbysubstr(date,1,4)havingsum(je)>750
awk-F"|"'$2=="工资"{a[substr($1,1,4)]+=$4}END{for(iina)if(a[i]>750)printi,a[i]}'file
1、统计出2007年1月份发生额总和大于2000的客户,列出帐号、姓名、月份、发生额合计
selecta.acct,a.name,substr(b.date,1,6),sum(b.je)fromkhxxa,mxbwherea.acct=b.acctandsubstr(b.date,1,6)='200701'groupbya.acct,a.name,substr(b.date,1,6)havingsum(b.je)>2000
awk-F,'NR==FNR&&substr($2,1,6)=="200701"{a[$1]+=$4}NR>FNR&&a[$1]>2000{print$1,$2,"200701",a[$1]}'mx.txtkhxx.txt
1010002李四2007012950
awk如何实现sql语句的group分组功能呢?
关键是定义好数组,如:第1例中sql对月份(substr(date,1,6))分组,那awk中就定义数组a[substr($1,1,6)]。至于要给该数组赋怎样的值,看统计需求。
如例1统计分组后的次数,就a[substr($1,1,6)]++,表示a[substr($1,1,6)]=a[substr($1,1,6)]+1;
若要合计金额,如例2,则a[$2)]+=$4,等价于a[$2]=a[$2]+$4,$4表示第4字段,是金额字段;至于例3,又稍微复杂了点,要根据两个条件分组(月份substr($1,1,6)、操作员$6),那定义的数组就是a[substr($1,1,6)""$6],注意下标中的"",是为了输出时显示效果
1、同时指定多个分割符
这时应该把分隔符写成放到方括号中,如$awk-F'[:/t]''{print$1,$3}'test
此时指定了空格,:号,tab三个作为分隔符
2、awk的key的变态用法
awk'{a[$1,"/t",$2]+=$4}END{for(uinina)printf("%s/t%d/n",uin,a[uin])}'test
用$1"/t"$2组成一维数组的key,这样可以用这种方式来处理很多复杂的二维数据逻辑
3、awk的范围模板
范围模板匹配从第一个模板的第一次出现到第二个模板的第一次出现之间所有行。如果有一个模板没出现,则匹配到开头或末尾。
如$awk'/root/,/mysql/'test将显示root第一次出现到mysql第一次出现之间的所有行。
4、awk的重定向
awk可使用shell的重定向符进行重定向输出,如:$awk'$1=100{print$1>"output_file"}'test。上式表示如果第一个域的值等于100,则把它输出到output_file中。也可以用>>来重定向输出,但不清空文件,只做追加操作。
这样可以利用重定向,可以把不同的结果集写入到不同的文件里
比如,我经常要跑出vip1,2,3,4,5,6的6份文件,那么就可以写一个脚本,一次性都跑出来了
5、awk-F"|"'NR==FNR{a[$1]=$2}NR>FNR{if(a[$1]!=""){a[$1]=$2-a[$1];if(a[$1]>0&&$2==2)print$0;}}'testtest1
还有涉及2个文件的时候,NR,FNR一起用,也比较少见的