awk理论详解、实战
答疑解惑:
为什么用awk取IP的时候用$4? ifconfig eth0 | awk -F '[ :]+' 'NR==2{print $4}' IP第二行内容如下: inet addr:10.0.0.41 Bcast:10.0.0.255 Mask:255.255.255.0 # 因为分隔符-F的'+',表示将多个空格或冒号当成一个对待,此时大多数人都会认为$1是inet, 你不是说把多个空格当成一个,那就把前面的空格都去掉就ok么.实际情况是: # 分隔符前后必须有内容,你把前面这么多空格当成一个,那它后面有字段,前面没字段,它去分割谁? 所以:以空格开头的(不管是一个还是多个)行,空格将是第一个字段. echo " 1 2 3 4" | awk -F '[: ]+' '{print $1,$2}' 1
1.基本结构
awk BEGIN{coms}/pattern/{coms}END{coms}
开始模块 /找谁/{干什么} 结束模块
a.BEGIN和END模块最多各出现一次
b./找谁/{干什么},可以是多个,比如:
NR==2{print $1}NR==5{print $2}
2.常用内置变量
$0:当前记录,一整行;
$1,$2,$3...$n:第n个字段,字段由FS分隔;
FS(filed separator):输入字段分隔符,默认是空格;
OFS:输出字段分隔符,默认为空白字符;
cat test.txt ABC:123:Jack DEF:456:Alice GHI:789:Amy # 使用内置变量时,要使用-v选项来指定对应的变量 awk -v FS=":" -v OFS="=" '{print $1,$2}' test.txt
NF(number of filed):字段数,有多少字段,$NF代表最后一个字段;
NR(Number of Record):行号,从1开始;
FNR:当前输入文件的记录数目.
# 将/etc/passwd中第一行的第一个元素和第七个元素调换位置 awk -F ":" 'NR==1{print $7":"$2":"$3":"$4":"$5":"$6":"$1}' /etc/passwd # 取出输出内容的第三行的倒数第二列 df -hT | awk 'NR==3{print $(NF-1)}' #取/etc/passwd第10行到20行的第三列,写入到test.txt中 awk -F ":" 'NR>9 && NR<21{print $3}' /etc/passwd | cat -n awk -F ":" 'NR>9 && NR<21{print $3}' /etc/passwd > test.txt # 用双引号不行
3.不常用内置变量
RS(Record Separator):输入记录分隔符(输入换行符),默认为换行符;
ORS(Out Record Separator):输出记录分隔符(输出换行符),指定输出时的换行符;
FILENAME:当前文件名;
ARGV:数组,保存的是命令行所给定的各参数;
ARGC:ARGC数组的个数.
# 遇见了RS指定的分隔符就换行 awk -v RS=":" '{print NR,$0}' test.txt # 遇见了换行符就把\n替换成ORS指定的分隔符 awk -v ORS="---" '{print $0}' test.txt ABC:123:Jack---DEF:456:Alice---GHI:789:Amy---[root@nfs01 ~]# awk 'BEGIN{print ARGV[0],ARGV[1],ARGV[2],ARGC}' a.txt b.txt awk a.txt b.txt 3 # 自定义变量: # 使用-v来自定义变量,-v varname=value awk -v myVar="testvar" 'BEGIN{print myVar}' # 在program中直接定义,定义变量和action之间需要用;隔开 awk 'BEGIN{myVar="testvar";print myVar}'
4.awk格式化:
# 先介绍一下printf printf "%s\n" abc def ghi jkl abc def ghi jkl printf "( %s )" 1 18 66;echo"" ( 1 )( 18 )( 66 ) a.默认不会换行,如果需要,可以在"格式替换符"后加"\n"进行转义; b."替换符"和"被格式化的文本"之间需要用"逗号"隔开; c."替换符"和"被格式化的文本"得一一对应. awk -v FS=":" 'BEGIN{printf "%-20s\t %s\n" , "User","UID"} {printf "%-20s\t %s\n" , $1,$3}' /etc/passwd # "-"表示左对齐,不加默认右对齐 netstat -anp|awk '$6=="LISTEN" || NR==1 {printf "%-10s %-10s %-10s \n",$1,$2,$3}'
5.模式:也就是条件,要找谁
# 关系运算符模式: awk 'NR>3 && NR<6 {print $0}' /etc/passwd # 正则模式:awk '/正则表达式/{print xx}' filename awk '/\/bin\/bash$/{print $0}' /etc/passwd # 当使用{x,y}类型次数匹配的正则表达式时,需要使用--posix选项或--re-interval选项 awk --posix '/hel{2,3}o/{print $0}' test3 hello helllo # 行范围模式: # awk '/正则1/,/正则2/{动作}' filename # 从被正则1匹配到的行开始,到被正则2匹配到的行结束,之间所有行都会执行对应的动作, # 这种模式被称为行范围模式 # 想要从如下文本中找出,网卡1的地址在192.168.0.0、16网段的主机 cat test5 主机名 网卡1的IP 网卡2的IP 主机A 192.168.1.123 192.168.1.124 主机B 192.168.2.222 172.16.100.2 主机C 10.1.0.1 172.16.100.3 主机D 10.1.5.1 192.168.1.60 awk '$2~/192\.168\.[0-9]{1,3}\.[0-9]{1,3}/{print $1,$2}' test5
6.控制语句
# 必须用在{}中,且比较内容用()括起来 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 awk -F: 'NR!=1{if($3<500) {print $1,"Suser"} else {print $1,"Nuser"}}' /etc/passwd awk 'BEGIN{for(i=1;i<=6;i++){print i}}' # while循环是只有满足条件时才执行对应语句; awk 'BEGIN{i=1;while(i<=6){print i;i++}}' # do...while循环是无论是否满足条件,先执行一遍do对应的代码, # 然后再判断是否满足while对应的条件,满足则执行do对应的代码;不满足则不执行. awk 'BEGIN{i=1;do{print "hehe";i++}while(i<=5)}'
a.continue和break
# 注意这条命令中的括号,当i的值为3时,跳过当前循环: awk 'BEGIN{for(i=1;i<=5;i++) {if (i==3){continue};print i}}' #当i的值为3时,跳出整个循环: awk 'BEGIN{for(i=1;i<=5;i++) {if (i==3){break};print i}}'
b.exit、next
# 如果没有在END模式使用exit,则直接跳出awk命令: awk 'BEGIN{print 1;exit;print 2}' # 如果在END模式中使用了exit,则直接执行END,跳过其它动作: awk 'BEGIN{print "start";exit}{print $0}END{print "over"}' test5 # next让awk不对匹配到的行进行操作,直接处理下一行: awk '{if (NR==2){next} print $0}' test5
c.条件、逻辑、数值
# 条件表达式 awk -F: '$1=="mysql"{print $3}' /etc/passwd # 逻辑运算符 awk -F: '$1~/mail/ && $3>8 {print }' /etc/passwd # 逻辑与 awk -F: '$1~/mail/ || $3>1000 {print }' /etc/passwd # 逻辑或 # 数值运算 awk -F: '/root|mail/{print $3+10}' /etc/passwd 10 18 21 # 这条命令为什么打印了三行,因为下面这条命令会匹配到三行 awk -F: '/root|mail/' /etc/passwd root:x:0:0:root:/root:/bin/bash mail:x:8:12:mail:/var/spool/mail:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin
7.实际应用
应用1: # 计算/etc目录下,普通文件的大小,使用KB作为单位 ls -l|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",int(sum/1024),"KB"}' 应用2: # 统计netstat -anp 状态为LISTEN和CONNECT的连接数量分别是多少? netstat -anp|awk '$6~/LISTEN|CONNECTED/{sum[$6]++} END{for (i in sum) printf "%-10s %-6s %-3s \n", i," ",sum[i]}' 应用3: # 统计/etc目录下不同用户的普通文件的总数是多少? ls -l|awk 'NR!=1 && !/^d/{sum[$3]++}END{for (i in sum) printf "%-6s %-5s %-3s \n",i," ",sum[i]}' # 统计/etc目录下不同用户的普通文件的大小总size是多少? ls -l|awk 'NR!=1 && !/^d/{sum[$3]+=$5}END{for (i in sum) printf "%-6s %-5s %-3s %-2s \n",i," ",sum[i]/1024/1024,"MB"}' 应用4: # ftp|http|mysql的端口号是多少 awk -F "[ /]+" '$1~/^(ftp|http|mysql)$/{print $1,$2}' /etc/services | uniq # ^http$:前后都加上,表示只查找指定内容,其实不加也行 # 计算/etc/services文件中的空行 grep -c "^$" /etc/services awk '/^$/{a=a+1;print a}END{print a}' /etc/services awk '/^$/{a=a+1}END{print a}' /etc/services 应用5: cat test0 Marry 2143 78 84 77 Jack 2321 66 78 45 Tom 2122 48 77 71 Mike 2537 87 97 95 Bob 2415 40 57 62 awk 'BEGIN{math=0;eng=0;com=0;printf "Lineno. Name No. Math English Computer Total\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 Lineno. Name No. Math English Computer Total ------------------------------------------------------------ 1 Marry 2143 78 84 77 239 2 Jack 2321 66 78 45 189 3 Tom 2122 48 77 71 196 4 Mike 2537 87 97 95 279 5 Bob 2415 40 57 62 159 ------------------------------------------------------------ Total: 319 393 350 Avg: 63.8 78.6 70
参考linux awk详解:https://www.cnblogs.com/xudong-bupt/p/3721210.html