参考文章awk命令详解
一、概述
awk是专门为文本处理设计的编程语言,是一门数据驱动的编程语言,与sed类似都是以数据驱动的行处理软件,主要用于数据扫描、过滤、统计汇总工作,数据可以来自标准输入、管道或者文件。
awk在20世纪70年代诞生与贝尔实验室。现在使用的版本是1988年发布的Gnu awk。
二、基础语法
2.1 记录与字段
awk是一种处理文本文件的编程语言,文件的每行数据都被称为记录,默认以空格或制表符为分隔符,每条记录被分成若干字段(列),awk每次从文件中读取一条记录。
语法格式:
1 | awk [选项] ‘条件{动作} 条件{动作} ... ...’ 文件名 |
awk语法由一系列条件和动作组成,在花括号内可以有多个动作,多个动作之间用分号分隔,在多个条件和动作之间可以有若干空格,也可以没有。
如果没有指定条件则匹配所有数据,如果没有指定动作则默认为print打印。
awk是对文档中每一行的记录执行的
2.2 内置变量
常用awk内置变量
| 变量名 | 描述 |
|---|---|
| FILENAME | 当前输入的文档名称 |
| FNR | 当前输入文档的当前行号 |
| NR | 输入数据流的当前行号 |
| $0 | 当前行的全部数据 |
| $n… | 当前行的第n个字段的内容(n>=1) |
| NF | 当前记录(行)的字段(列)个数 |
| FS | 字段分隔符,默认为空格或者tab制表符 |
| OFS | 输出字段分隔符,默认为空格 |
| ORS | 输出记录分隔符,默认为换行符\n |
| RS | 输入记录分隔符,默认为换行符\n |
代码示例
1 | free | awk '{print $2}' #逐行打印第2列 |
FNR和NR的区别
同样是输出行号,NR将所有文件的数据视为一个数据流,而FNR则是将多个文件的数据视为独立的若干个数据流,遇到新文件时行号从1开始重新递增。

2.3 自定义变量
1 | awk -v key1="value1" -v key2=value2 '{print key1,key2}' |
使用例

2.4 调用系统变量
1 | awk -v 自定义变量名=$系统变量名 '{print 自定义变量名}' |
使用例

2.5 自定义分隔符
默认以空格、换行符、制表符作为分隔符,使用-F可以指定分隔符
指定自定分隔符后对于每行文本来说,指定的字符将作为该行每列的分隔标准
1 | awk -F: '{print $1}' /etc/passwd #以冒号作为分隔符输出第一列 |
2.6 内置变量RS、OFS、ORS
RS
内置变量RS保存的是输入数据的行分隔符,默认为\n,可以指定其它字符作为行分隔符
1 | awk -v RS="." '{print $1}' /etc/hosts #指定.作为行分隔符 输出每一行的第一列 |
使用例

OFS
保存的是输出字段的分隔符(列分隔符),默认为空格
1 | awk -v OFS="-" '{print $1,$2}' /etc/hosts #以"-"作为字段分隔符 |
ORS
保存的是每行输出记录的分隔符
1 | awk -v ORS="----\n" '{print $1}' /etc/hosts |
使用例

2.7 print指令
可以输出常量和变量,如果是字符串常量需要用双引号括起来,数字常量可以直接打印
1 | awk '{print 123}' /etc/hosts |
使用例

2.8 条件匹配
awk支持使用正则进行模糊匹配,也支持字符串和数字的精确匹配,并且支持逻辑与和逻辑或。
| 比较符号 | 描述 |
|---|---|
| // | 全行数据正则匹配 |
| !// | 对全行数据正则匹配后取反 |
| ~// | 对特定数据正则匹配 |
| !~// | 对特定数据正则匹配后取反 |
| == | 等于 |
| != | 不等于 |
| > | 大于 |
| >= | 大于等于 |
| < | 小于 |
| <= | 小于等于 |
| && | 逻辑与 |
| || | 逻辑或 |
1 | awk '/localhost/' /etc/hosts |
2.9 BEGIN和END
BEGIN导致动作指令仅在读取任何数据记录之前执行一次,END导致动作指令仅在读取完所有数据记录后执行一次
BEGIN可以进行数据初始化,END可以进行数据汇总
1 | awk 'BEGIN{print "OK"}' |
使用例

1 | awk -F: 'BEGIN{print "用户名 UID 解释器"} \ |
使用例

2.10 数字计算
1 | awk 'BEGIN{print 2+3}' |
awk中变量不需定义就可以直接使用,作为字符处理时未定义的变量默认值为空,作为数字处理时未定义的变量默认值为0
1 | awk 'BEGIN{print "["x"]","["y"]"}' #x和y默认为空 |
使用例

2.11 循环计数
逐行读取/etc/passwd文件,x初始值为0,匹配到以bash结尾的行时自加1,最后打印x的值。
1 | awk '/bash$/{x++} END{print x}' /etc/passwd |

统计有多少个客户端登录root
1 | who | awk '$1=="root"{x++} END{print x}' |

打印1~200之间能被6整除且包含数字6的整数数字
1 | seq 200 | awk '$1%6==0 && $1~/6/' |

三、awk条件判断
if判断后面如果只有一个动作指令,则花括号{}可省略,如果if判断后面的指令为多条指令则需要使用花括号括起来,多个指令使用分号分隔。
3.1 单分支语句
语法:
1 | if(判断条件){ |
查找cpu使用率大于0.3的进程
1 | ps -eo user,pid,pcpu,comm | awk '{if($3>0.5) print}' |

3.2 双分支if语句
语法:
1 | if(判断条件){ |
统计系统用户和普通用户的个数
1 | awk -F: '{if($3<1000){x++} else{y++}} END{print "系统用户个数:"x"","普通用户个数:"y""}' /etc/passwd |

四、awk数组与循环
4.1 数组声明与赋值
awk数组在声明的同时可以赋值,未赋值默认为空字符串,下标可以为字符串
语法:
1 | 数组名[下标]=值 #声明 |
使用例
1 | awk 'BEGIN{a[0]=11;a[1]=12;print a[0],a[1]}' |

4.2 遍历数组
使用in-for遍历数组,无序
语法:
1 | for(变量 in 数组名){ |
使用例:
1 | awk 'BEGIN{ \ |

4.3 for循环
语法:
1 | for(表达式1;表达式2;表达式3) { |
使用例:
1 | awk 'BEGIN{ for (i=1;i<=4;i++) {print i}}' |

统计root出现的次数。
这里面包含了两个循环,一个是隐含循环,awk会逐行处理数据;一个是for循环每列的值,如果等于root,就让x自加1,最后打印x的值
1 | awk -F: '{ \ |

4.4 while循环
语法:
1 | while(条件判断){ |
使用例:
1 | awk 'BEGIN{ i=1; while(i<=5) {print i;i++}}' |
4.5 中断语句
与shell类似,awk提供了continue、break、exit循环中断语句。
使用例:
1 | awk 'BEGIN{ \ |

五、awk函数
5.1 内置I/O函数
getline函数
能让awk立刻读取下一行数据(读取下一条记录并复制给$0,并重新设置NF、NR和FNR)
使用例:
1 | #解决挂载逻辑卷时,分区信息跨行显示的问题 |
next函数
停止处理当前的输入记录,立刻读取下一条记录并返回awk程序的第一个模式匹配重新处理数据。
有点类似于循环语句中的continue,不会执行当次循环的后续语句
与next函数与getline函数对比:
getline:
1 | awk -F: '/root/{getline;print "next line:",$0} {print "normal line"}' /etc/passwd |

next:
1 | awk -F: '/root/{next;print "next line:",$0} {print "normal line"}' /etc/passwd |

经比较可以看出,getline,会继续执行后续的指令print “next line:”,而next不会执行后续指令,而是重新开始匹配
system(命令)函数
可以直接在awk中调用shell命令,会启动一个新shell进程执行命令
1 | awk 'BEGIN{system("ls")}' |
5.2 内置数值函数
1 | awk 'BEGIN{print cos(0)}' #余弦 |
5.3 内置字符串函数
1 | #length |
5.4 内置时间函数
1 | #systime() 返回当前时间距离1970-01-01 00:00:00有多少秒 |
5.5 用户自定义函数
语法:
1 | function 函数名(参数列表) { 命令序列 } |
使用例:
1 | awk ' \ |
六、常用命令
1 | cat example.txt | awk 'NR%2==1' #删除example.txt文件中的所有偶数行 |
七、常用技巧
7.1 打印各个磁盘的可用大小
1 | df | grep -v tmpfs | awk 'NR!=1 {disk[$1]=$4} \ |
7.2 统计磁盘的可用容量
1 | df | tail -n +2 | grep -v tmpfs | awk '{sum+=$4} END{print "磁盘可用容量:"sum/1024/1024"G"}' |
7.2 统计/etc下文件总大小
1 | ls -l /etc | awk '/^-/{sum+=$5} END{print "文件总大小:"sum/1024"M"}' |
7.2 统计访问Nginx的各IP访问次数
1 | awk ' \ |
7.2 查看Nginx 1点到5点半的日志
1 | awk -F"[: /]" '$7":"$8 >= "01:00" && $7":"$8 <="05:30"' /var/log/nginx/access.log |
7.2 查看Docker容器的CPU使用率
1 | docker stats jenkins --no-stream |awk 'NR==2{print $3}' |