Fork me on GitHub

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

posted @ 2018-11-26 17:03  法外狂徒  阅读(410)  评论(0编辑  收藏  举报