shell awk教程

awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来格式化文本

1. awk命令形式:

awk [-F|-f|-v] 'BEGIN{ commands } pattern{ commands } END{ commands }' file
[-F|-f|-v]   大参数,-F指定分隔符,-f调用脚本,-v定义变量 var=value
'  '          引用代码块
BEGIN   初始化代码块,在对每一行进行处理之前,初始化代码,主要是引用全局变量,设置FS分隔符
pattern //    匹配代码块,可以是字符串或正则表达式
{}           命令代码块,包含一条或多条命令
;          多条命令使用分号分隔
END      结尾代码块,在对每一行进行处理之后再执行的代码块,主要是进行最终计算或输出结尾摘要信息

1.1. awk处理过程:

BEGIN{ commands } pattern{ commands } END{ commands }

第一部分:BEGIN{ commands }。

该语句块不依赖于文件,是在读取文件之前,执行该语句中的语句块,常用于变量的初始化,打印输出表格的表头。

第二部分:pattern{ commands }

它逐行从从文件或标准输入读取内容,从第一行到最后一行,每读取一行,执行一次pattern{ commands }语句块,如果没有pattern语句块,则默认运行{ print },即打印每个读取到的行

第三部分:END{ commands }

此语句是在文件所有行被读取完成后执行,常用于打印全部行的分析结果这类信息汇总都是在END语句块中完毕

说明:上面三个部分,至少要有一个

1.2. 预定义变量:

$0           表示整个当前行
$1           每行第一个字段
NF          字段数量变量
NR          每行的记录号,多文件记录递增
FNR        与NR类似,不过多文件记录不递增,每个文件都从1开始
\t            制表符
\n           换行符
FS          BEGIN时定义分隔符
RS       输入的记录分隔符, 默认为换行符(即文本是按一行一行输入)
~            匹配,与==相比不是精确比较
!~           不匹配,不精确比较
==         等于,必须全部相等,精确比较
!=           不等于,精确比较
&&     逻辑与
||             逻辑或
+            匹配时表示1个或1个以上
/[0-9][0-9]+/   两个或两个以上数字
/[0-9][0-9]*/    一个或一个以上数字
FILENAME   文件名
OFS        输出字段分隔符, 默认也是空格,可以改为制表符等
ORS        输出的记录分隔符,默认为换行符,即处理结果也是一行一行输出到屏幕
print  #print $1$2 无空格拼接$1$2, print $1,$2 会有输出分割符隔开$1和$2

1.3. -f指定脚本文件

]# awk -f script.awk  file
]# cat script.awk 
BEGIN{
FS=":"
}
{print $1}              
 //效果与awk -F":" '{print $1}'相同,只是分隔符使用FS在代码自身中指定

1.4. -F指定分隔符

-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\t Uid:" $3 }' /etc/passwd       //自定义输出  
awk -F: '{print NF}' /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 '{print NR,$0}' /etc/passwd          //输出每行的行号
awk -F: '{print NR,NF,"\t",$NF}' /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}'                    //不显示第一行
]# echo "1 2|3 4|5"|awk -F'[|]+' '{print "$1="$1,"\n$2="$2,"\n$3="$3}'
$1=1 2 
$2=3 4 
$3=5

1.5. -v 定义变量

]# user="root"
]# awk -F: -vu=$user '$1==u' /etc/passwd  #过滤出第一列是root的行
root:x:0:0:root:/root:/bin/bash

1.6. 模糊匹配

用~表示包含,!~表示不包含

打印出第一列包含root的行
]# awk -F: '$1~/roo/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
打印出最后一列不含bash,且不包含login的行
]# awk -F: '$NF!~/bash/ && $NF!~/login/' /etc/passwd
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt

1.7. //匹配代码块 正则匹配

//纯字符匹配 !//纯字符不匹配 ~//字段值匹配 !~//字段值不匹配 ~/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 #输出匹配mysql或mail的行
awk '!/mysql|mail/{print}' /etc/passwd #输出不匹配mysql和mail的行
awk -F: '/mail/,/mysql/{print}' /etc/passwd   #区间匹配,匹配mail的行到匹配mysql的行
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 #与上面相同,$1匹配指定内容才显示
awk -F: '$1!~/mail/{print $1}' /etc/passwd     #$1不匹配包含mail的行
awk -F: '$1!~/mail|mysql/{print $1}' /etc/passwd #$1不匹配包含mail和mysql的行    

1.8. 条件表达式

==   !=   >   >=  
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                         //小于等于

1.9. 逻辑运算符

&& || 
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 

1.10. 数值运算

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/{print int($2/1024)}' /proc/meminfo           //取整

1.11. 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: -va=3 -vb=4 'BEGIN{if(a>b) print "a>b";else if(a==b) print "a==b";else print "a100 ? "yes":"no")}'  /etc/passwd 
awk -F: '{print ($3>100 ? $3":\tyes":$3":\tno")}'  /etc/passwd

1.12. 循环

与shell类似,awk提供了在循环中使用continue、break、exit循环中断语句。

1.12.1. while循环语句

]# awk 'BEGIN{sum=0;i=1;while(i<5){if(i/2==0)continue;sum+=i;i++}; print sum}'

1.12.2. do while循环语句

awk 'BEGIN{sum=0;i=1;do{sum+=i;i++}while(i<=5); print sum}'

1.12.3. for循环语句

]# awk 'BEGIN{sum=0;for(i=1;i<5;i++){sum+=i};print sum}'

15

]# awk 'BEGIN{sum=0;i=1;for(i=1;i<=5;i++){if(i%2==0)continue;sum+=i}; print sum}'

9

]# awk '{a[$1]++} END{for(i in a) print i,a[i]}' access.log|sort -k2 -nr

1.13. 输出分隔符OFS

awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
awk '$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt      
awk '$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt      
//输出字段6匹配WAIT的行,其中输出每行行号,字段4,5,6,并使用制表符分割字段
]# echo "1 2 3"|awk '{NF=NF;print $0}' OFS="|"   #NF=NF;加上此设置,就可以让$0以OFS分隔
1|2|3

1.14. 输出处理结果到文件

①在命令代码块中直接输出 route -n|awk 'NR!=1{print > "./fs"}'

②使用重定向进行输出 route -n|awk 'NR!=1{print}' > ./fs
 

1.15. 格式化输出

print	: 打印
	printf: 格式化打印
  %: 格式化输出分隔符,每个%对应后面一个要打印的变量
	%s: 字符串,
	%d: 数字
	-: 左对齐
	+: 右对齐
	15: 至少占用15字符	
 
awk中函数(格式化打印)
案例1:
awk -F: '{printf "|%-15s | %-10d|\n", $NF, $3}' /etc/passwd
 
解析:
|		: 以 | 为分隔符
|%-15s|	 : 以 | 为分隔符, 字符串占用15个字符位,靠左对齐
|%-10d|	 : 以 | 为分隔符,数字占用10个字符位,靠左对齐
\n		 : 换行符
$NF		 : 最后一列

1.16. 数组

awk数组是关联数组,下标可以是数字/字母/字符等,而shell普通数组的下标必须是数字,shell的关联数组的下标也可以是任意字符

#遍历数组:  for(变量 in 数组名){ 动作指令序列 },输出是无序的。如for(i in a),i是索引,a[i]是值
|]# awk '{a[$1]++} END{for(i in a) print i,a[i]}' access.log|sort -k2 -nr
172.168.1.3 4
172.168.1.1 3
172.168.1.2 2
#数组成员关系判断
]# awk 'BEGIN{ a[11]=1;a[22]=2; if("22" in a){print "yes"} else {print no}  }'
yes

1.17. awk调用shell命令

1.17.1. 通过管道符号、双引号调用shell命令

#调用wc -l命令统计使用bash 的用户个数,等同于grep -c "bash$" /etc/passwd
]# awk -F: '/bash$/{print|"wc -l"}' /etc/passwd

8

#调用system("") 来调用命令

awk 'BEGIN{system("ls")}'

]# awk -F: '/^root/{system("echo `date +%F` "$1)}' /etc/passwd

2024-06-18 root

1.18. awk内置函数

1.18.1. getline函数

能让awk立刻读取下一行数据(读取下一条记录并复制给$0,并重新设置NF、NR和FNR)

#打印偶数行
]# echo -e "1\n2\n3\n4"|awk '{getline;print $0}'
2
4

1.18.2. next函数

停止处理当前的输入记录,立刻读取下一条记录并返回awk程序的第一个模式匹配重新处理数据。 有点类似于循环语句中的continue,不会执行当次循环的后续语句

]# echo -e "1\n2\n3\n4"|awk '$1=="2"{next}{print $1}'
1
3
4
]# echo -e "1\n2\n3\n4"|awk '{if($1=="2")next;print $1}'
1
3
4

1.18.3. 内置数值函数

cos(expr)、sin(expr)、sqrt(expr)

1.18.3.1. int(expr)函数,可以对小数取整
]# awk 'BEGIN{print int(6.8)}'
6
1.18.3.2. rand()函数,返回0到1之间的随机数
awk 'BEGIN{print rand()}' 
awk 'BEGIN{for(i=1;i<=5;i++) print int(100*rand())}'	#生成5个100以内的随机数

1.18.4. 内置字符串函数

1.18.4.1. length([s])函数: 可以统计字符串s的长度,如果不指定字符串s则统计$0的长度
awk 'BEGIN{test="hello"; print length(test)}'		#打印字符串长度
awk 'BEGIN{t[0]="hi";t[1]="the"; print length(t)}'	#返回数组元素个数
awk '{print length()}' /etc/shells					#返回文件每行的字符长度
1.18.4.2. index(字符串1,字符串2): 返回字符串2在字符串1中的位置
]# awk 'BEGIN{test="hello";print index(test,"l")}'
3
1.18.4.3. match(s,r): 根据正则表达式r返回其在字符串s中的位置坐标
awk 'BEGIN{print match("How much","[a-z]")}' #小写字母在第2个位置开始出现
2
1.18.4.4. tolower(srt): 可以将字符串转换为小写
]# awk 'BEGIN{print tolower("HELLo")}'
hello
1.18.4.5. toupper(str): 将字符串转为大写
]# awk 'BEGIN{print toupper("HELLo")}'
HELLO
1.18.4.6. split(字符串,数组,分隔符)

将字符串按特定的分隔符切片后存储在数组中,如果没指定分隔符,则使用IFS定义的。 数组下标从1开始

awk 'BEGIN{split("hello world",test); print test[1],test[2]}'
awk 'BEGIN{split("hello:world",test,":"); print test[1],test[2]}'	#指定冒号(:)为分隔符
1.18.4.7. gsub(r,s,[,t])

将字符串t中所有与正则表达式r匹配的字符串全部替换为s,如果没有指定字符串t,则默认对$0进行替换操作

[15:11:47][root@localhost:~]# head -1 /etc/passwd | awk '{gsub("[0-9]","**");print $0}'
root:x:**:**:root:/root:/bin/bash
1.18.4.8. sub(r,s,[,t])

与gsub类似,但仅替换第一个匹配的字符串,而不是替换全部

1.18.4.9. substr(s,i,[,n])

对字符串s进行截取,从第i位开始,截取n个字符串,如果n没有指定则一直截取到字符串s的末尾位置

]# awk 'BEGIN{hi="Hello World"; print substr(hi,2,3)}'  #从第2位开始截取3个字符
ell

1.18.5. 内置时间函数

systime() 返回当前时间距离1970-01-01 00:00:00有多少秒

]# awk 'BEGIN{print systime()}'
1627802328

1.18.6. 用户自定义函数

语法:function 函数名(参数列表) { 命令序列 }

]# awk 'function max(x,y){if(x>y){print x}else{print y}}BEGIN{max(3,5)}'
5

2. 举例

cat example.txt | awk 'NR%2==1'  #打印奇数行
cat example.txt | awk 'NR%2==0'  #打印偶数行
#制表符分隔输出多字段
awk -F'[:#]' '{print $1,$2,$3,$4,$5,$6,$7}' OFS='\t' helloworld.sh 
#制表符分隔输出多字段,效果同上  
awk -F'[:#]' '{NF=NF;print $0}' OFS='\t' helloworld.sh  
//计算/root目录下,普通文件的大小,使用KB作为单位
]# ls -l /root|awk 'BEGIN{sum=0} NR>1{sum+=$5} END{print "total size is:",sum/1024,"KB"}' 
total size is: 10680.3 KB
]# ls -l /root/|awk 'BEGIN{sum=0} !/^d/{sum+=$5} END{print "total size is:",int(sum/1024),"KB"}'
total size is: 10672 KB
#统计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]}'
#统计/etc/passwd中不同登录shell的数量
]# awk -F: '{sum[$NF]++}END{for(i in sum)print i,sum[i]}' /etc/passwd|sort -k2 -nr
/sbin/nologin 24
/bin/bash 8
/sbin/shutdown 1
/sbin/halt 1
/bin/sync 1
#统计文件中空行数
]# awk 'BEGIN{X=0} /^$/{ X+=1 } END{print "I find",X,"blank lines."}' test 
I find 4 blank lines.
#统计访问Nginx的各IP访问次数
]# awk '{IP[$1]++}END{for(i in IP){print i,IP[i]}}' /var/log/nginx/access.log
#查看Nginx 1点到5点半的日志
]# awk -F"[: /]" '$7":"$8 >= "01:00" && $7":"$8 <="05:30"' /var/log/nginx/access.log
---------------------------------------------------------------------------------------------------------
#输出成绩表
]# 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运维,#shell,运维,linux,ssh)