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一起用,也比较少见的