AWK
什么是awk
awk是一个编程语言
主要作用: 对文本和数据的处理
awk处理数据的流程
1.扫描文件内容,从上到下进行扫描,按照行进行处理
2.寻找匹配到的内容,进行读取到特定的模式中,进行行处理
3.行满足指定模式动作,则输出到屏幕上面,不满足丢弃
4.接着读取下一行继续处理,接着循环,直到文件的结尾
awk语法格式
awk [选项] command files
awk '[pattern]{COMMAND}' file
pattern 可以没有,默认每一个
{COMMAND} 也可以为空,默认 print $0
但是 pattern 和 {COMMAND} 不能同时为空
三种格式
行处理前 行处理中 行处理后
BEGIN 执行的命令 最后的显示 END
#行处理前
[root@shell01 ~]# awk 'BEGIN{print 1 + 1}'
2
#行处理前+行处理中 (-F是简写)
[root@shell01 ~]# awk -F: '{print $1}' /etc/passwd
root
bin
#-F为处理前,等于BEGIN{FS=':'} {print $1}为行处理中
[root@shell01 ~]# awk 'BEGIN{FS=":"}{print $1}' /etc/passwd
#输出分隔符默认为空格,这里修改为#
[root@shell01 ~]# awk 'BEGIN{FS=":";OFS="#"}{print $1,$2}' /etc/passwd
root#x
bin#x
daemon#x
#行处理前+行处理中+行处理后 (END为行处理后,会在最后执行)
[root@shell01 ~]# awk 'BEGIN{FS=":";OFS="#"}{print $1,$2}END{print "内容打印结束"}' /etc/passwd
root#x
bin#x
...
内容打印结束
#行处理前+行处理中+行处理后 (文件有几行,就打印几行OK)
[root@shell01 ~]# awk 'BEGIN{print 1+1}{print "OK"}END{print "内容打印结束"}' /etc/hosts
2
OK
OK
内容打印结束
[root@shell01 ~]# cat test.txt
10 10 10
[root@shell01 ~]# awk '{print $1+$2}' test.txt
20
[root@shell01 ~]# awk -F: '{print $1,$3}' /etc/passwd
root 0
bin 1
awk的工作原理
1.awk将文件中的每一行作为输入,将每一行的数据赋值给内部变量 $0
2.awk开始进行字段分解,根据指定的分隔符,将每个字段进行赋值给内部变量 $1 $2 $3
3.awk默认的分隔为空白字符,由内部变量FS来确定,也可以使用-F进行指定对应的分隔符
4.awk处理时使用print进行打印已分隔后的字段
5.awk打印输出结果之后,不同字段之间由空格进行分隔,这个空格是由不同字段之间的逗号进行映射的,由内部变量OFS进行修改,OFS默认的输出分隔符就是空格
6.awk输出一行内容后,重新读取下一行内容,会覆盖内部变量$0,然后新的字符进行字段分割并进行处理。
#awk的命令格式
匹配 模式 动作
#匹配 模式(root就是模式)
[root@shell01 shell13]# awk '/root/' passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
#动作
[root@shell01 shell13]# awk '{print $1}' passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
#模式+动作
[root@shell01 shell13]# awk -F: '/root/{print $1}' passwd
root
operator
#当根分区使用率大于多少则打印Available
[root@shell01 shell13]# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda3 18863104 1875848 16987256 10% /
devtmpfs 487048 0 487048 0% /dev
[root@shell01 ~]# df |awk '/\/$/{print $4}'
16987256
[root@shell01 ~]# df |awk '/\/$/{ if ($3>180000) print $4}'
16987256
Awk的内部变量
1. 输入分隔符
FS 输入分隔符 默认以空白字符为分隔符
[root@shell01 ~]# awk -F: '{print $1}' passwd
[root@shell01 ~]# awk 'BEGIN{FS=":"}{print $1}' passwd
#指定多个分隔符, :和空格
[root@shell01 ~]# awk -F '[ :]' '{print $3}' passwd
#获取ip地址
[root@shell01 shell13]# ip a s eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:49:ee:cc brd ff:ff:ff:ff:ff:ff
inet 10.0.0.2/24 brd 10.0.0.255 scope global noprefixroute eth0
#注意:行开头空格前面也算一个字符;如果碰到' /'空格和斜杠一起,算一个分割符
[root@shell01 ~]# ip a s eth0|awk -F '[ /]*' 'NR==3{print $3}'
2. 输出分隔符
OFS 默认输出分隔为空格, 打印的时候用逗号作为分割,逗号就是输出分隔符,在显示的时候,默认映射为空格。
[root@shell01 shell13]# awk -F: '{print $1,$2}' passwd
root x
bin x
[root@shell01 shell13]# awk -F: '{print $1$2}' passwd
rootx
binx
#-v指定内部变量,分隔符为@
[root@shell01 shell13]# awk -F: -vOFS="@" '{print $1,$2}' passwd
root@x
bin@x
[root@shell01 shell13]# awk -F: 'BEGIN{OFS="$"}{print $1,$2}' passwd
root@x
bin@x
3. 内部变量NF 最后一列
# NF 表示每一行最后一列(列数) $NF 打印最后一列的内容
[root@shell01 shell13]# awk -F: '{print $NF}' passwd
/bin/bash
/sbin/nologin
/sbin/nologin
[root@shell01 shell13]# awk -F: '{print NF}' passwd
7
7
4. 内部变量$0
$0 表示整行内容
#没有过滤,打印所有内容
[root@shell01 shell13]# awk '{print $0}' passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
#取第一行
[root@shell01 shell13]# awk 'NR==1{print $0}' passwd
root:x:0:0:root:/root:/bin/bas
5. 内部变量NR
NR 表示是行号 $NR不可用
#NR显示行号
[root@shell01 shell13]# awk '{print NR,$0}' passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
#第一行
[root@shell01 shell13]# awk 'NR==1' passwd
root:x:0:0:root:/root:/bin/bash
[root@shell01 shell13]# awk 'NR==1{print $0}' passwd
root:x:0:0:root:/root:/bin/bash
6. 内部变量 FNR
#FNR 显示行号,针对多个文件时进行处理
不同的文件显示各自的行号
#下面两个文件行号叠加了,没有区分
[root@shell01 shell13]# awk '{print NR,$0}' passwd test.txt
1 root:x:0:0:root:/root:/bin/bas
...
10 2: eth0: <BROADCASE,MU...
#下面两个文件行号各区计算
[root@shell01 shell13]# awk '{print FNR,$0}' passwd test.txt
1 root:x:0:0:root:/root:/bin/bas
...
1 2: eth0: <BROADCASE,MU...
#了解
#7. RS 输入记录分隔符 ORS 输出记录分隔符
8. awk格式化输出
[root@shell01 ~]# date |awk '{print "今年是: "$NF"年","今月为: "$2"月"}'
今年是: 2024年 今月为: Jun月
[root@shell01 ~]# awk -F: '{print "用户为: "$1,"UID为:"$3,"GID为: "$4}' /etc/passwd
用户为: root UID为:0 GID为: 0
用户为: bin UID为:1 GID为: 1
用户为: daemon UID为:2 GID为: 2
printf 函数输出(了解)
%表示内容 -表示左对齐 20表示占20个字符 s表示字符类型
[root@shell01 ~]# awk -F: '{printf "%-20s %-10s %-10s\n", "用户为: "$1,"UID为:"$3,"GID为: "$4}' /etc/passwd
用户为: root UID为:0 GID为: 0
用户为: bin UID为:1 GID为: 1
用户为: daemon UID为:2 GID为: 2
Awk模式动作
1. 正则表达式
#获取root开头的
[root@shell01 ~]# awk '/^root/' /etc/passwd
#获取每一行等于root开头的,和上面效果相同,~表示等于
[root@shell01 ~]# awk '$0 ~/^root/' /etc/passwd
#获取第4列以root开头的
[root@shell01 ~]# awk -F: '$4 ~/^root/' /etc/passwd
#取反,不以root开头
[root@shell01 ~]# awk '!/^root/' /etc/passwd
[root@shell01 ~]# awk '$0 !~/^root/' /etc/passwd
#不取第4列以root开头的
[root@shell01 ~]# awk -F: '$4 !~/^root/' /etc/passwd
2. 比较表达式
关系运算符
< 小于
<= 小于等于
== 等于
> 大于
>= 大于等于
!= 不等于
[root@shell01 shell13]# awk -F: '$3==0' passwd
root:x:0:0:root:/root:/bin/bash
#下面{print $0}可以省略,默认打印该行
[root@shell01 shell13]# awk -F: '$5<0{print $0}' passwd
#字符串比较 (字符串最好用引号引起来)
[root@shell01 shell13]# awk -F: '$1=="root"' passwd
root:x:0:0:root:/root:/bin/bash
#不等于
[root@shell01 shell13]# awk -F: '$4 != "root"' passwd
[root@shell01 ~]# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda3 18863104 1875848 16987256 10% /
devtmpfs 487048 0 487048 0% /dev
[root@shell01 ~]# df |awk '/\/$/'|awk '$3 > 1800000{print $4}'
16987148
3. 条件表达式
#如果第3列大于2万就打印整行
[root@shell01 ~]# awk -F: '{if($3>20000)print $0}' /etc/passwd
#每一行第3列大于1000打印最后一列,否则打印第一列
[root@shell01 ~]# awk -F: '{if($3>1000){print $NF} else {print $1}}' /etc/passwd
4. 运算表达式
[root@shell01 ~]# awk -F: '$3 * 100 > 50000' /etc/passwd
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
#如果$3乘以10大于1000打印最后一列,否则打印第一列
[root@shell01 ~]# awk -F: '{if($3*10>1000){print $NF} else {print $1}}' /etc/passwd
root
bin
[root@shell01 shell13]# awk -F: '/sync/{print $3}' passwd
5
[root@shell01 shell13]# awk -F: '/sync/{print $3 + 10}' passwd
15
[root@shell01 shell13]# awk -F: '/sync/{print $3*2}' passwd
10
[root@shell01 shell13]# awk -F: '/sync/{print $3/5}' passwd
1
[root@shell01 shell13]# awk -F: '/sync/{print $3^5}' passwd
3125
[root@shell01 shell13]# awk -F: '/sync/{print $3%5}' passwd
0
[root@shell01 shell13]# awk -F: '/sync/{print $3,$4}' passwd
5 0
[root@shell01 shell13]# awk -F: '/sync/{print $3+$4}' passwd
5
5. 逻辑操作符
&& 并且
|| 或者
! 非
#行数大于2并且行数小于4
[root@shell01 shell13]# awk 'NR>2 && NR<4' passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@shell01 shell13]# awk 'NR>2 || NR<4' passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
#不为1的行
[root@shell01 shell13]# awk 'NR!=1' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
#第1列为root且第三列大于10
[root@shell01 shell13]# awk -F: '$1 ~/root/ && $3 > 10' passwd
#不以root开头
[root@shell01 shell13]# awk '!/^root/' passwd
Awk判断语句
if
#1.打印系统管理员用户的数量
[root@shell01 ~]# awk -F: '$3==0' /etc/passwd |wc -l
#通过判断第三列是否为零,i进行累计,最后输出i
[root@shell01 ~]# awk -F: '{if($3==0){i++}}END{print "当前系统管理员用户的数量:"i}' /etc/passwd
当前系统管理员用户的数量:1
if else
#打印系统管理员的数量和其他用户的数量
[root@shell01 ~]# awk -F: '{if($3==0){i++}else{j++}}END{print "当前系统管理员用户数量:"i,"当前系统其他用户数量:"j}' /etc/passwd
当前系统管理员用户数量:1 当前系统其他用户数量:23
#这里END中写两条print命令,用;隔开
[root@shell01 ~]# awk -F: '{if($3==0){i++}else{j++}}END{print "当前系统管理员用户数量:"i;print "当前系统其他用户数量:"j}' /etc/passwd
当前系统管理员用户数量:1
当前系统其他用户数量:23
if else if else
#打印系统管理员的数量和系统用户的数量及普通用户的数量 (小于1000系统用户,大于1000普通用户)
[root@shell01 ~]# awk -F: '{if($3==0){a++}else{if($3>0 && $3<1000){b++}else{c++}}}END{print "管理员数量:"a,"系统用户数量:"b,"普通用户数量:"c}' /etc/passwd
管理员数量:1 系统用户数量:22 普通用户数量:1
Awk循环语句
while循环
[root@shell01 shell13]# awk 'BEGIN{ i=1; while(i<=3){print i; i++}}'
1
2
3
#相当于每一行,都要循环到总列数,并打印1-列数
[root@shell01 ~]# awk -F: '{i=1; while(i<=NF){print i;i++}}' /etc/passwd
#每一行都打印10次
[root@shell01 ~]# awk -F: '{i=1; while(i<=10){print $0;i++}}' /etc/passwd
for循环
#把每行所有内容一个个打印出来
[root@shell01 shell13]# awk -F: '{for(i=1;i<=NF;i++){print $i}}' passwd
awk数组
将需要统计的某个字段作为数组的索引,然后对索引进行遍历
#统计数组 (把最后一列作为索引)
[root@shell01 ~]# awk -F: '{shell[$NF]++}' /etc/passwd
#计算次数
[root@shell01 ~]# awk -F: '{shell[$NF]++}END{for(i in shell)print i"出现了"shell[i]"次"}' /etc/passwd
/bin/sync出现了1次
/bin/bash出现了1次
/sbin/nologin出现了20次
/sbin/halt出现了1次
/sbin/shutdown出现了1次
#站访问状态统计
[root@shell01 shell13]# ss -an
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
nl UNCONN 0 0 0:0 *
nl UNCONN 0 0 0:5717 *
nl UNCONN 0 0 0:5717 *
nl UNCONN 4352 0 4:7971 *
[root@shell01 ~]# ss -an|awk '{status[$2]++}END{for(i in status)print i,status[i]}'
LISTEN 38
ESTAB 90
State 1
UNCONN 63