当前位置: 首页 > Linux

Linux基本命令介绍八:文本分析awk

时间:2023-04-06 20:24:12 Linux

awk是一种模式扫描和处理语言,是分析和处理数据时非常强大的工具。awk[options]'pattern{action}'file...awk的工作过程如下:逐行读取输入(标准输入或文件),对匹配模式pattern的行执行action。当模式省略时,表示匹配任意字符串;action省略时表示执行'{print}';它们不能同时被省略。每行输入对于awk来说都是一条记录,awk用$0来指代当前记录:[root@centos7~]#head-1/etc/passwd|awk'{print$0}'root:x:在0:0:root:/root:/bin/bash的例子中,命令head-1/etc/passwd作为awk的输入,awk省略了模式,动作为print$0,表示打印当前记录。对于每条记录,awk使用分隔符将其分成列,第一列用$1表示,第二列用$2表示...最后一列用$NF表示Option-F表示指定的分隔符如输出文件/etc/passwd的第一行第一列(用户名)和最后一列(登录shell):[root@centos7~]#head-1/etc/passwd|awk-F:'{print$1,$NF}'root/bin/bash当没有指定分隔符时,使用一个或多个空格(空白字符,由空格键或TAB键生成)作为分隔符。输出分隔符默认为空格。例如在输出命令ls-l*的结果中,文件大小和文件名:[root@centos7temp]#ls-l*|awk'{print$5,$NF}'13b.txt58c.txt12d.txt0e.txt0f.txt24test.sh[root@centos7temp]#你也可以过滤任何列:[root@centos7temp]#ls-l*|awk'$5>20&&$NF~/txt$/'-rw-r--r--1nobodynobody58November1616:34c.txt其中$5>20表示第五列大于20;&&表示逻辑与;$NF~/txt$/中,~表示Match,符号//里面是正则表达式。这里省略动作,整个awk语句的意思是打印文件大小大于20字节,文件名以txt结尾的行。awk用NR表示行号[root@centos7temp]#awk'/^root/||NR==2'/etc/passwdroot:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologin[root@centos7temp]#中例如,||表示逻辑或,语句表示:输出文件/etc/passwd中以root开头的行或第二行。在某些情况下使用awk过滤甚至比使用grep更灵活,比如在ifconfig的输出中获取网卡名称及其对应的mtu值[root@idc-v-71253~]#ifconfig|awk'/^\S/{print$1"\t"$NF}'ens32:1500ens33:1500lo:65536[root@idc-v-71253~]##这里的正则表达式是指不以空白字符开头的行,和输出格式为\t。上面提到的NR、NF等都是awk的内置变量,下面列出了一些常用的内置变量$0当前记录(这个变量存放的是整行的内容)$1~$n第n个当前记录的字段间隔由FS分隔FS输入字段分隔符默认为空白或TabNF。当前记录的字段数是多少列NR行号,从1开始,如果有多个文件,这个值也会不断累加。FNR输入文件行号RS输入记录分隔符,默认为换行符OFS输出字段分隔符,默认也是空格ORS输出记录分隔符,默认为换行符FILENAME当前输入文件名也可以使用awk中的自定义变量,比如把网卡的名字赋值给变量a,然后输出网卡的名字和它对应的RX字节的值(注意不同模式匹配和action的写法):[root@idc-v-71253~]#ifconfig|awk'/^\S/{a=$1}/RXp/{printa,$5}'ens32:999477100ens33:1663197120lo:0awk中有两种特殊模式:BEGIN和END;它们不会匹配输入的文本,BEGIN对应END的action部分组合成一个代码块,在任何输入开始之前执行;END对应的动作部分组合成一个代码块,在所有输入处理完成后执行。#注意类似C语言的赋值和打印函数用法[root@centos7temp]#ls-l*|awk'BEGIN{print"sizename\n--------"}$5>20{x+=$5;print$5,$NF}END{print"--------\ntotal",x}'sizename--------58c.txt24test.sh---------total82[root@centos7temp]#awk也支持数组,数组的索引都被当作字符串(即关联数组)处理。可以使用for循环遍历输出文件/etc/passwd中的数组元素各种登录shell及其总数#注意数组赋值和for循环遍历数组[root@centos7temp]#awk-F':''{a[$NF]++}END{for(iina)printi,a[i]}'/etc/passwd/bin/sync1/bin/bash2/sbin/nologin19/sbin/halt1/sbin/shutdown1[root@centos7temp]#当然还有if分支语句#注意花括号是如何定义actionblock的[root@centos7temp]#netstat-antp|awk'{if($6=="LISTEN"){x++}else{y++}}END{printx,y}'63[root@centos7temp]#模式可以用逗号分隔,表示从匹配第一个模式开始,直到匹配第二种模式[root@centos7~]#awk'/^root/,/^adm/'/etc/passwdroot:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologinadm:x:3:4:adm:/var/adm:/sbin/nologin也支持三元运算符pattern1?pattern2:pattern3,表示判断pattern1是否匹配,true匹配pattern2,false匹配pattern3,pattern也可以是类似C语言的表达式例如判断/etc/passwd文件中UID大于500的登录shell是否为/bin/bash,则输出整行,否则输出UID为0的行:#注意目录分隔符转义为了避免混淆[root@centos7~]#awk-F:'$3>500?/\/bin\/bash$/:$3==0{print$0}'/etc/passwdroot:x:0:0:root:/root:/bin/bashlearner:x:1000:1000::/home/learner:/bin/bash#三元运算符也可以嵌套,例子从略。选项-ffile表示从文件中读取awk指令#PrintFibonacci契约序列的前十项[root@centos7temp]#cattest.awkBEGIN{$1=1$2=1OFS=","for(i=3;i<=10;i++){$i=$(i-2)+$(i-1)}print}[root@centos7temp]#awk-ftest.awk1,1,2,3,5,8,13,21,34,55[root@centos7temp]#选项-F指定列分隔符#当使用多个字符作为分隔符时[root@centos7temp]#echo1.2,3:45|awk-F'[.,:]''{print$2,$NF}'25[root@centos7temp]##这里-F后面单引号里面的内容也是正则表达式选项-vvar=val设置变量#这里printf函数的用法类似于C语言中的同名函数[root@centos7~]#awk-vn=5'BEGIN{for(i=0;i和>>>将输出保存到文件#根据文件access.log拆分文件到第一列(IP)并将其保存到ip.txt文件[root@centos7temp]#awk'{print>$1".txt"}'access.log[root@centos7temp]#ls-l172.20.71.*-rw-r--r--1rootroot5297Nov2221:33172.20.71.38.txt-rw-r--r--1rootroot1236Nov2221:33172.20.71.39.txt-rw-r--r--1rootroot4533Nov2221:33172.20.71.84.txt-rw-r--r--1rootroot2328Nov2221:33172.20.71.85.txt内置functionlength()获取字符串长度[root@centos7temp]#awk-F:'{if(length($1)>=16)print}'/etc/passwdsystemd-bus-proxy:x:999:997:systemdBusProxy:/:/sbin/nologin[root@centos7temp]#split()将字符串用分隔符分开保存到数组中[root@centos7temp]#head-1/etc/passwd|awk'{split($0,arr,/:/);for(i=1;i<=length(arr);i++)printarr[i]}'rootx00root/root/bin/bash[root@centos7temp]#getline获取从输入记录(可以是管道、另一个文件或当前文件的下一行),将其分配给一个变量或重置一些环境变量]#awk'BEGIN{"date"|getline;split($5,arr,/:/);printarr[1]}'09#从文件中获取,此时会覆盖掉当前的$0(注意在逐行处理b.txt的同时,也是逐行从c.txt中获取记录并覆盖$0。当getline先遇到eof时<即c.txt文件行数较少>会输出一个空行)[root@centos7temp]#awk'{getline<"c.txt";print$4}'b.txt"https://segmentfault.com/blog/learning"[root@centos7temp]##赋值给变量[root@centos7temp]#awk'{getlineblog<"c.txt";print$0"\n"blog}'b.txtaasdasdadsadBLOGADDRESSIS"https://segmentfault.com/blog/learning[root@centos7temp]##读取下一行(也会覆盖当前的$0)[root@centos7temp]#catfileanny100bob150cindy120[root@centos7temp]#awk'{getline;total+=$0}END{printtotal}'file370#此时只有next的功能和getline类似。它还读取下一行并覆盖$0。不同的是,执行next后,不再执行后面的命令,而是读取下一行,从头开始执行。#跳过a-s开头的行,统计行数,打印最终结果[root@centos7temp]#awk'/^[a-s]/{next}{count++}END{printcount}'/etc/passwd2[root@centos7temp]##合并同一列两个文件的另一个例子[root@centos7temp]#catf.txt学号score000018000002750000390[root@centos7temp]#cate.txtname学号ZhangSan00001李四00002王五00003[root@centos7temp]#awk'NR==FNR{a[$1]=$2;next}{print$0,a[$2]}'f.txte.txt姓名和学生numberscore张三0000180李四0000275王五0000390#这里读取第一个文件时,NR==FNR成立,执行a[$1]=$2,接下来忽略后面的。读取第二个文件时,如果NR==FNR不成立,则执行后续打印命令sub(regex,substr,string)替换字符串string中匹配正则表达式的第一个子串substring(省略时为$0)substr[root@centos7temp]#echo178278world|awk'sub(/[0-9]+/,"hello")'helloworld[root@centos7temp]#gsub(regex,substr,string)类似于sub(),但不仅仅是第一个,而是全局替换[root@centos7temp]#head-n5/etc/passwd|awk'{gsub(/[0-9]+/,"----");print$0}'root:x:----:----:root:/root:/bin/bashbin:x:----:----:bin:/bin:/sbin/nologindaemon:x:----:----:daemon:/sbin:/sbin/nologinadm:x:----:----:adm:/var/adm:/sbin/nologinlp:x:----:----:lp:/var/spool/lpd:/sbin/nologinsubstr(str,n,m)切割字符串str,从第n个字符开始,切割m片。如果省略m,则走到最后[root@centos7temp]#echo"hello,world!"|awk'{printsubstr($0,8,1)}'boundary[root@centos7temp]#tolower(str)toupper(str)表示大小写转换[root@centos7temp]#echo"hello,world!"|awk'{A=toupper($0);printA}'HELLO,world![root@centos7temp]#system(cmd)执行shell命令cmd,并返回执行结果。执行成功则为0,失败则不为0#这里if语句的判断与C语言一致,0为假,非0为真[root@centos7temp]#awk'BEGIN{if(!system("date>/dev/null"))print"success"}'success[root@centos7temp]#match(str,regex)返回匹配字符串str中的位置正则表达式[root@centos7temp]#awk'BEGIN{A=match("abc.f.11.12.1.98",/[0-9]{1,3}\./);printA}'7[root@centos7temp]#作为一种编程语言,awk可以处理各种问题,甚至可以编写应用软件,但它更常用于命令行下的文本分析和报告生成。在这些场景中,awk的效果非常好。如果你在工作中经常需要进行文本分析,那么掌握这个命令的用法将会为你节省很多时间。