本案例要求使用awk工具完成下列过滤任务:
1>练习awk工具的基本用法2>提取本机的IP地址、根分区使用率3>格式化输出/etc/passwd文件中的用户名、UID、宿主目录信息
格式化输出passwd文件内容时,要求第一行为列表标题,最后一行提示一共已处理文本的总行数,如下图所示。
步骤:
步骤一:awk文本过滤的基本用法
1)基本操作方法
格式:awk[选项]'[条件]{编辑指令}'文件
其中,print是最常用的编辑指令;若有多条编辑指令,可用分号分隔。
处理文本时,若未指定分隔符,则默认将空格、制表符等作为分隔符。
直接过滤文件内容:
[root@svr5~]#awk'{print$1,$2}'/etc/rc.local//输出文件的第1、2列#!/bin/sh##This#You#wanttouch/var/lock/subsys/local
结合管道过滤命令输出:
[root@svr5~]#uname-a//正常的完整输出Linuxsvr5.tarena.com2.6.18-348.el5#1SMPWedNov2821:22:00EST2012x86_64x86_64x86_64GNU/Linux[root@svr5~]#uname-a|awk'{print$1,$3,$12}'//输出第1、3、12字段Linux2.6.18-348.el5x86_64
2)选项-F可指定分隔符
截取/etc/passwd文件的前7行,用来创建一个测试文件,操作如下:
[root@svr5~]#head-7/etc/passwd>passwd.txt[root@svr5~]#catpasswd.txtroot: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/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologinsync:x:5:0:sync:/sbin:/bin/syncshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
输出passwd.txt文件中以分号分隔的第1、7个字段,显示的不同字段之间以逗号隔开,操作如下:
[root@svr5~]#awk-F:'{print$1","$7}'passwd.txtroot,/bin/bashbin,/sbin/nologindaemon,/sbin/nologinadm,/sbin/nologinlp,/sbin/nologinsync,/bin/syncshutdown,/sbin/shutdown
或者:
[root@svr5~]#awk-F":"'{print$1","$7}'passwd.txtroot,/bin/bashbin,/sbin/nologindaemon,/sbin/nologinadm,/sbin/nologinlp,/sbin/nologinsync,/bin/syncshutdown,/sbin/shutdown
awk还识别多种单个的字符,比如以“:”或“/”分隔,输出第1、10个字段:
[root@svr5~]#awk-F[:/]'{print$1,$10}'passwd.txtrootbashbinnologindaemonnologinadmsbinlpsyncsyncshutdownshutdown
输出每次处理的行号,以及当前行以“:”分隔的字段个数:
[root@svr5~]#awk-F:'{printNR,NF}'passwd.txt17273747576777
3)awk处理的时机
awk会逐行处理文本,支持在处理第一行之前做一些准备工作,以及在处理完最后一行之后做一些总结性质的工作。在命令格式上分别体现如下:
行前处理,BEGIN{}逐行处理,{}行后处理,END{}
上述编辑指令段可以包含在一对单引号内,比如:
awk[选项]'BEGIN{编辑指令}{编辑指令}END{编辑指令}'文件
只做预处理的时候,可以没有操作文件,比如:
[root@svr5~]#awk'BEGIN{A=1024;printA*2.56}'2621.44
[root@svr5~]#awk'BEGIN{x=0}/\ 当然,这个例子比较简单(效果与egrep-c'\ 步骤二:利用awk提取本机的IP地址、根分区使用率 1)提取IP地址 分步实现的思路及操作参考如下—— 通过ifconfigeth0查看网卡信息,其中包括IP地址: [root@svr5~]#ifconfigeth0eth0Linkencap:EthernetHWaddr00:0C:29:82:09:E9inet192.168.4.4Bcast:192.168.4.255Mask:255.255.255.0inet6addr:fe80::20c:29ff:fe82:9e9/64Scope:LinkUPBROADCASTRUNNINGMULTICASTMTU:1500Metric:1RXpackets:358524errors:0dropped:0overruns:0frame:0TXpackets:230638errors:0dropped:0overruns:0carrier:0collisions:0txqueuelen:1000RXbytes:44470760(42.4MiB)TXbytes:64236279(61.2MiB) 结合grep获得包含IP地址的那一行: [root@svr5~]#ifconfigeth0|grep"inet"inetaddr:192.168.4.4Bcast:192.168.4.255Mask:255.255.255.0 再结合awk过滤出第2列: [root@svr5~]#ifconfigeth0|grep"inet"|awk'{print$2}'192.168.4.4 2)提取根分区使用率 通过df命令查看根分区的使用情况,其中包括使用率: [root@svr5~]#df-hT/文件系统类型容量已用可用已用%挂载点/dev/sda2ext319G7.2G11G40%/ 输出上述结果中最后一行的第6列: [root@svr5~]#df-hT/|tail-1|awk'{print$6}'40% 步骤三:格式化输出/etc/passwd文件 1)任务需求及实现思路分析 根据任务要求的结果,输出的内容包括三个部分:列表头、用户信息、列表尾。 由于/etc/passwd文件中的用户记录是单一的以“:”分隔,而且恰好awk本身就已经支持“前、中、后”三段式处理,所以用awk处理是再合适不过了。通过awk的内置变量NR即可获得处理的记录行数,因此只要设置正确的输出即可。 2)根据实现思路编写、验证awk过滤语句 输出信息时,可以使用“\t”显示Tab制表位: [root@svr5~]#awk-F:'BEGIN{print"User\tUID\tHome"}\{print$1"\t"$3"\t"$6}END{print"Total"NR"lines."}'/etc/passwdUserUIDHomeroot0/rootbin1/bindaemon2/sbinadm3/var/admlp4/var/spool/lpdsync5/sbin....iamkiller1234/opt/.private/iamkillernsd0010/home/nsd001nsd0021236/home/nsd002nsd0031237/home/nsd003postfix89/var/spool/postfixTotal67lines. 二、awk处理条件 本案例要求使用awk工具完成下列过滤任务,注意awk处理条件的设置: 1>列出UID间于501~505的用户详细信息2>输出/etc/hosts文件内以127或192开头的记录3>列出100以内整数中7的倍数或是含7的数 步骤一:认识awk处理条件的设置 创建测试文件passwd.txt文件: 1)使用正则表达式设置条件 输出其中以bash结尾的完整记录: [root@svr5~]#awk-F:'/bash$/{print}'passwd.txtroot:x:0:0:root:/root:/bin/bash 输出以a、b、c或d开头的用户名、宿主目录: [root@svr5~]#awk-F:'/^[a-d]/{print$1,$6}'passwd.txtbin/bindaemon/sbinadm/var/adm [root@svr5~]#awk-F:'/^a|nologin$/{print$1,$7}'passwd.txtbin/sbin/nologindaemon/sbin/nologinadm/sbin/nologinlp/sbin/nologin 输出其中宿主目录以bin结尾(对第6个字段做~匹配)的用户名、宿主目录信息: [root@svr5~]#awk-F:'$6~/bin$/{print$1,$6}'passwd.txtbin/bindaemon/sbinsync/sbinshutdown/sbin [root@svr5~]#awk-F:'$7!~/nologin$/{print$1,$7}'passwd.txtroot/bin/bashsync/bin/syncshutdown/sbin/shutdown 2)使用数值/字符串比较设置条件 输出第3行(行号NR等于3)的用户记录: [root@svr5~]#awk-F:'NR==3{print}'passwd.txtdaemon:x:2:2:daemon:/sbin:/sbin/nologin 输出奇数行(行号NR除以2余数为1)的用户记录: [root@svr5~]#awk-F:'NR%2==1{print}'passwd.txtroot:x:0:0:root:/root:/bin/bashdaemon:x:2:2:daemon:/sbin:/sbin/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologinshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 输出偶数行(行号NR除以2余数为0)的用户记录: [root@svr5~]#awk-F:'NR%2==0{print}'passwd.txtbin:x:1:1:bin:/bin:/sbin/nologinadm:x:3:4:adm:/var/adm:/sbin/nologinsync:x:5:0:sync:/sbin:/bin/sync 输出前3行文本: [root@svr5~]#awk-F:'NR<=3{print}'passwd.txtroot:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologin 输出从第5行开始到文件末尾的所有行: [root@svr5~]#awk-F:'NR>=5{print}'passwd.txtlp:x:4:7:lp:/var/spool/lpd:/sbin/nologinsync:x:5:0:sync:/sbin:/bin/syncshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 输出用户名为“sync”的行: [root@svr5~]#awk-F:'$1=="sync"{print}'passwd.txtsync:x:5:0:sync:/sbin:/bin/sync [root@svr5~]#awk-F:'$1==ENVIRON["USER"]{print$1,$6,$7}'passwd.txtroot/root/bin/bash 3)逻辑测试条件 输出第3~5行文本: [root@svr5~]#awk-F:'NR>=3&&NR<=5{print}'passwd.txtdaemon:x:2:2:daemon:/sbin:/sbin/nologinadm:x:3:4:adm:/var/adm:/sbin/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologin 严谨一点可以写成: [root@svr5~]#awk-F:'(NR>=3)&&(NR<=5){print}'passwd.txtdaemon:x:2:2:daemon:/sbin:/sbin/nologinadm:x:3:4:adm:/var/adm:/sbin/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologin 输出第3行和第5行文本: [root@svr5~]#awk-F:'NR==3||NR==5{print}'passwd.txtdaemon:x:2:2:daemon:/sbin:/sbin/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologin [root@svr5~]#awk-F:'$7!~/nologin$/||$1~/^[ad]/{print}'passwd.txtroot:x:0:0:root:/root:/bin/bashdaemon:x:2:2:daemon:/sbin:/sbin/nologinadm:x:3:4:adm:/var/adm:/sbin/nologinsync:x:5:0:sync:/sbin:/bin/syncshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 输出UID小于3或者UID是偶数的用户记录: [root@svr5~]#awk-F:'$3<3||$3%2==0{print}'passwd.txtroot:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologinlp:x:4:7:lp:/var/spool/lpd:/sbin/nologinshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 4)数学运算 以统计passwd.txt文件中以“:”分隔的总字段个数,需要每处理一行时将当前行的字段数(内置变量NF)计和,因此可在BEGIN时定义一个初始变量,过程称求和,最后在END时输出结果。 [root@svr5~]#awk-F:'BEGIN{x=0}\{x+=NF}END{print"Total"x"fields."}'passwd.txtTotal49fields. 步骤二:完成任务要求的awk过滤操作 1)列出UID间于501~505的用户详细信息: [root@svr5~]#awk-F:'$3>=501&&$3<=505{print}'/etc/passwdhunter:x:501:501::/home/hunter:/bin/bashvina:x:502:502::/home/vina:/bin/bashkdev:x:503:503::/home/kdev:/bin/bashzengye:x:504:504::/home/zengye:/bin/bashstu01:x:505:1201::/tech/nsdhome/stu01:/bin/bash 2)输出/etc/hosts映射文件内以127或者192开头的记录: [root@svr5~]#awk-F:'/^127|^192/{print}'/etc/hosts127.0.0.1localhostlocalhost.localdomainlocalhost4localhost4.localdomain4192.168.4.5svr5.tarena.comsvr5 3)列出100以内整数中7的倍数或是含7的数: 此操作无处理文件,正常思路应该是用Shell循环来完成;因为要求用awk来实现,如果不用循环,则根据逐行处理的思路,应该提供一个100行的文本对象,然后将行号作为处理的整数,逐个判断并输出即可。 利用seq命令可生成1-100的整数序列,比如: [root@svr5~]#seq10012345678910.... 结合管道交给awk处理,可以简化实现步骤。针对本任务而言,行号与每行的实际文本值是一致的,那么根据NR或者$0行值进行判断都是可以的。输出100以内7的倍数或是包含7的数: [root@svr5~]#seq100|awk'NR%7==0||NR~/7/{print}'7141721272835374247.... [root@svr5~]#seq100|awk'$0%7==0||$0~/7/{print}'7141721272835374247.... 三、awk综合脚本应用 本案例要求编写脚本getupwd-awk.sh,实现以下需求: 步骤一:任务需求及思路分析 编写getupwd-awk.sh脚本的任务要求如下: 步骤二:根据实现思路编写脚本 复制原getupwd.sh脚本,生成getupwd-awk.sh: [root@svr5~]#catgetupwd.sh//确认原脚本内容#/bin/bash>/tmp/getupwd.log##创建空文件sed-n'/:\/bin\/bash$/w/tmp/urec.tmp'/etc/passwd##提取符合条件的账号记录UNUM=$(egrep-c'.'/tmp/urec.tmp)##取得记录个数while[${i:=1}-le$UNUM]##从第1行开始,遍历账号记录doUREC=$(sed-n"${i}p"/tmp/urec.tmp)##取指定行数的记录NAME=${UREC%%:*}##截取用户名(记录去尾)PREC=$(sed-n"/^$NAME:/p"/etc/shadow)##查找与用户名对应的密码记录PASS=${PREC#*:}##掐头PASS=${PASS%%:*}##去尾,只留下密码记录echo"$NAME-->$PASS">>/tmp/getupwd.log##保存结果leti++##自增1,转下一次循环done/bin/rm-rf/tmp/urec.tmp##删除临时文件echo"用户分析完毕,请查阅文件/tmp/getupwd.log"##完成后提示[root@svr5~]#cpgetupwd.shgetupwd-awk.sh//复制为新脚本文件 修改新脚本文件,内容参考如下: [root@svr5~]#vimgetupwd-awk.sh#/bin/bash##创建空文件>/tmp/getupwd.log##提取用户名列表awk-F:'/:\/bin\/bash$/{print$1}'/etc/passwd>/tmp/users.tmp##通过for循环遍历用户名、查询密码记录,保存结果forNAMEin$(cat/tmp/users.tmp)dogrep"^$NAME:"/etc/shadow|awk-F:'{print$1"-->"$2|\"cat>>/tmp/getupwd.log"}'/etc/shadowdoneecho"用户分析完毕,请查阅文件/tmp/getupwd.log"##完成后提示[root@svr5~]#chmod+xgetupwd-awk.sh 步骤三:验证、测试脚本 [root@svr5~]#./getupwd-awk.sh用户分析完毕,请查阅文件/tmp/getupwd.log[root@svr5~]#head-5/tmp/getupwd.logroot-->$6$IWgMYmRACwdbfwBo$dr8Yn983nswiJVw0dTMjzbDvSLeCd1GMYjbvsDiFEkL8jnXOLcocBQypOCr4C6BRxNowIxjh6U2qeFU0u1LST/zengye-->$6$Qb37LOdzRl5995PI$L0zTOgnhGz8ihWkW81J.5XhPp/l7x2./Me2ag0S8tRndCBL9nIjHIKkUKulHxJ6TXyHYmffbVgUT6pbSwf8O71clamav-->!!mysql-->!!abc-->!!.... 四、awk流程控制 本案例要求了解awk的流程控制操作,可自行设置awk语句来有验证以下操作: 1>if分支结构(双分支、多分支)2>while循环结构3>break、continue等其他控制语句 步骤一:awk过滤中的if分支结构 1)单分支 统计/etc/passwd文件中UID小于或等于500的用户个数: [root@svr5~]#awk-F:'BEGIN{i=0}{if($3<=500){i++}}END{printi}'/etc/passwd39 统计/etc/passwd文件中UID大于500的用户个数: [root@svr5~]#awk-F:'BEGIN{i=0}{if($3>500){i++}}END{printi}'/etc/passwd28 [root@svr5~]#awk-F:'BEGIN{i=0}{if($7~/bash$/){i++}}END{printi}'\/etc/passwd29 [root@svr5~]#awk-F:'BEGIN{i=0}{if($7!~/bash$/){i++}}END{printi}'\/etc/passwd38 2)双分支 分别统计/etc/passwd文件中UID小于或等于500、UID大于500的用户个数: [root@svr5~]#awk-F:'BEGIN{i=0;j=0}{if($3<=500){i++}else{j++}}END{printi,j}'/etc/passwd3928 [root@svr5~]#awk-F:'BEGIN{i=0;j=0}{if($7~/bash$/){i++}else{j++}}\END{printi,j}'/etc/passwd2938 3)多分支 [root@svr5~]#awk-F:'BEGIN{i=0;j=0;k=0}{if($7~/bash$/){i++}\elseif($7~/nologin$/){j++}else{k++}}END{printi,j,k}'/etc/passwd29335 步骤二:awk过滤中的while循环结构 1)while循环 统计/etc/passwd文件内“root”出现的次数。 ——分析:以“:”或“/”做分隔,针对每一行的每一列进行比对,如果包含“root”,则次数加1。其中,逐行处理直接由awk完成,逐列处理交给while循环,通过i变量依次取$1、$2、……、$NF进行检查;变量j在预处理时赋值0,没匹配一个字段加1。 [root@svr5~]#awk-F[:/]\'BEGIN{j=0}\{i=1}{while(i<=NF){if($i~/root/){j++};i++}}\END{printj}'/etc/passwd4 此例仅为说明while循环的用法。 实际应用时,上述操作可以简单处理,可通过命令替换将文件内容赋值给一个变量(变为一行文本),然后针对此变量值以目标字符串“root”作为分隔,获取总字段数-1即可得目标字符串的总数量: [root@svr5~]#echo$(cat/etc/passwd)|awk-F"root"'{printNF-1}'4 五、awk扩展应用 本案例要求使用awk工具完成下列两个任务: 方案: 1)awk统计Web访问排名 在分析Web日志文件时,每条访问记录的第一列就是客户机的IP地址,其中会有很多重复的IP地址。因此只用awk提取出这一列是不够的,还需要统计重复记录的数量并且进行排序。 通过awk提取信息时,利用IP地址作为数组下标,每遇到一个重复值就将此数组元素递增1,最终就获得了这个IP地址出现的次数。 步骤一:统计Web访问量排名 分步测试、验证效果如下所述。 1)提取IP地址及访问量 2)对第1)步的结果根据访问量排名 附加: awk数据过滤软件【统计功能】;类似grep【过滤一整行】 awk[选项]'条件{指令}'文件条件可以没有【默认为所有】指令可以没有【打印整行】不能条件指令都没有awk默认使用空格或Tab键作为分割符 print$0#读一行就打印当前一整行print$1#读一行就打印第一列print$2#读一行就打印第二列...printNR#读一行就打印当前行的行号printNF#读一行就打印当前行的列数#awk-F:'{printNR}'/etc/passwd#读一行就打印一次当前的行号#awk-F:'{printNF}'/etc/passwd#读一行就打印当前行所有的列数#awk-F:'{print$NF}'/etc/passwd#读一行就打印当前行最后一列 打印常量(字符串需要引号)awk'{print"dachui"}'/etc/passwd//这个文件有多少行就打印多少行的dachui#awk-F:'{print"第"NR"行","有"NF"列"}'/etc/passwd打印多少行有多少列#awk-F:'{print"第"NR"行","有"NF"列"}'/etc/passwd|head-34|tail-1只打印第34行有几列 awk'BEGIN{}条件{}END{}'文件#三个条件不一定要全有,有一个就可以所有的指令都需要放到{}里BEGIN{}里的指令,是在读取文件之前,执行1次{}里的指令,在读取文件过程中执行,执行N次END{}里的指令,是在读取文件之后,执行1次 #awk-F:'BEGIN{print"用户名\tUID\t家目录"}{print$1"\t"$2"\t"$3}END{print"总用户量:"NR}'/opt/a.txt这个可以在执行这个文本之前打印BEGIN{}、END{}里面的内容 -F指定分隔符awk-F:'{print$1}'/etc/passwd#指定':'为分隔符awk-F,'{print$1}'/etc/passwd#指定','为分隔符 awk'/正则/{指令}'文件df|awk'/\/$/{print$4}'#打印以/结尾的行的第四列 过滤查看正在占用的内存#awk'/Failed/{print$11}'/var/log/secure#过滤出尝试破解密码的IP地址#free|awk'/Mem/{print$3}' 过滤查看正在占用的CPU使用率[1分钟,5分钟,15分钟]#uptime20:43:58up24min,3users,loadaverage:0.00,0.03,0.09#uptime|awk'{print$10}'#打印5分钟的cpu使用率0.03, awk[选项]'条件{指令}'文件 条件:1、/正则/awk'/root/'/etc/passwd #awk-F:'/root/'/etc/passwdroot:x:0:0:root:/root:/bin/bashoperator:x:11:0:operator:/root:/sbin/nologin #awk-F:'$1~/root/'/etc/passwd#~表示正则匹配,$1表示第一列,匹配第一列里面有root的行,打印出来root:x:0:0:root:/root:/bin/bash 2、字串和数字比较==、!=、<、<=、>、>=(精确匹配)awk-F:'$1=="root"'/etc/passwd#将root用户打印出来awk-F:'$3<=10'/etc/passwd#将UID小于等于10的用户打印出来,UID<=1000即系统用户#awk-F:'$3<=10'/etc/passwd#将UID大于等于1000的用户打印出来,UID>=1000即普通用户#awk'NR==2{print}'/etc/passwd#输出第2行文本#awk'NF>=6{print}'/etc/passwd#输出列数大于或等于6的行#awk'$2!="XX"{print}'/etc/passwd#输出第2行文本不是XX的行#awk'/^(127|192)/'/etc/hosts#输出开头是127或192开头的行 3、逻辑判断&&||#awk-F:'$3>10&&$3<20'/etc/passwd#awk-F:'$3==0||$3==1000'/etc/passwd 4、算术运算能被7整除或包含7的数(1--200)#seq200|awk'$1%7==0||$1~/7/'#$1~/7/用到正则模糊匹配,这个数列只有1列所以是$1,匹配含有7的选项 awk'BEGIN{x=0;printx%8}'awk'BEGIN{x=0;printx+8}'awk'BEGIN{x=0;printx-8}'awk'BEGIN{x=0;printx*8}'awk'BEGIN{x=0;printx/8}' awk支持if语句if(判断){指令}if(判断){指令}else{指令}if(判断){指令}elseif(){指令} 统计系统用户和普通用户分别是多少(1000)awk-F:'{}END{printx,y}'/etc/passwd#这里可以将if条件句分出来写,然后再复制进去,以防止括号太多导致错误if($3>=1000){x++}else{y++}最终结果:awk-F:'{if($3>=1000){x++}else{y++}}END{printx,y}'/etc/passwd 正则:/正则/对整行匹配$n~/正则/对某一列匹配$1~/root/对第一列匹配是否包含root