一、AWK介绍
awk格式由 参数+模式+ 动作组成
- 参数:如-F 分隔符等
- 模式:即pattern,可以理解为sed的匹配模式,可以是表达式,也可以理解成是一个条件
- 动作:action,是由大括号内的一条或者多条语句组成,用逗号分开
awk [options] 'pattern {action}' file
处理的内容可以来自标准输入或者文本以及管道等
二、内置变量
变量 | 含义 | 功能 |
---|---|---|
FIELDWIDTHS | 由空格分隔的一列数字,定义了每个数据字段的确切宽度 | |
NR | number of record | 输入流的当前记录编号,已经读出的记录数 |
RS | 输入记录分隔符,默认为换行符 = 行分隔符 | |
ORS | 输出记录分隔符,默认为换行符 | |
NF | number of field | NF表示当前行分割后的最后一列(0 和NF均为内置变量)NF表示最后一个字段,NF表示当前行被分隔符切开以后,一共有几个字段。假如一行文本被空格分成了7段,那NF值就是7 |
FS | field separator | 输入字段分隔符 = 区域分隔符,列分隔符 |
OFS | 输出字段分隔符,默认为空格 |
2.1 区域和记录
名称 | 含义 |
---|---|
field | 区域、字段,如$1 、$2 、$NF,$代表取或者引用的意思 |
record | 记录,默认一整行 如 $0 |
2.2 NR (number of record)行记录数
查看行号大于等于2的全部行 或者大于等于2小于10的行,并显示行号
awk 'NR>=2 {print NR,$0}' /etc/passwd
awk 'NR>=2&&NR<10 {print NR,$1,$NF}' /etc/passwd
或者
awk 'NR==2,NR==10 {print NR,$1,$NF}' /etc/passwd 也表示从第2-10行
2.3 NF (number of f)列记录数
NF代表按照某一个分隔符统计列的总数,$NF代表打印出最后一列内容
1) NF用法
按照句号分割,每一行共计多少列
[root@xiyu_master /root]
#cat date-2.log
2022.03.130000
20220313-18:00
18.00.12
[root@xiyu_master /root]
#cat date-2.log |awk -F . '{print NF}'
3
1
3
2) $NF用法
倒数最后一列
[root@xiyu_master /root]
#cat date-1.log |awk -F , '{print $NF}'
date2005
date2015
date2025
倒数第二列
[root@xiyu_master /root]
#cat date-1.log |awk -F , '{print $(NF-1)}'
date2004
date2014
date2024
2.3 FS与OFS 行分隔符的输入和输出
目标:将逗号(,)分隔符更换成 横杠(-)分隔符,FS代表输入分隔符,OFS代表输出的分隔符
[root@localhost ~]# cat date-1.log
date2000,date2001,date2002,date2003,date2004,date2005
date2010,date2011,date2012,date2013,date2014,date2015
date2020,date2021,date2022,date2023,date2024,date2025
#awk 'BEGIN{FS=","; OFS="-"} {print $1,$2,$3}' date-1.log
date2000-date2001-date2002
date2010-date2011-date2012
date2020-date2021-date2022
2.4 RS与ORS 列分隔符的输入和输出
变量 RS 和 ORS 定义了 awk 程序如何处理数据流中的字段,默认情况下,awk 将 RS 和 ORS 设为换行符。默认的 RS 值表明,输入数据流中的每行新文本就是一条新纪录。 有时,你会在数据流中碰到占据多行的字段。典型的例子是包含地址和电话号码的数据,其中地址和电话号码各占一行,例如:
RS代表行分隔符,RS=":",意思就是将目前:换成回车,RS=""代表将分隔符清空,就代表全部在一行
[root@xiyu_master /root]#cat information.log
xi yu
999 yuhang Street
Yuhang District
(0571)88219999
qing chen
999 yuhang Street
Yuhang District
(0572)88218888
xiao ye
998 yuhang Street
Yuhang District
(0573)88216666
将换行变成空格,输出1和4列
[root@xiyu_master /root]#awk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' information.log
xi yu (0571)88219999
qing chen (0572)88218888
xiao ye (0573)88216666
2.5 BEGIN与END
其中:BEGIN和END中的语句分别在开始读取文件(in_file)之前和读取完文件之后发挥作用,可以理解为初始化和扫尾。
awk [-参数 变量] ‘BEGIN{初始化}条件类型1{动作1}条件类型2{动作2}。。。。END{后处理}’
目标:取passwd中$3中大于20以上的行,使用到变量a进行递增,最后输出一个总数
awk -F ":" '$3>15{a++;print $0}' /etc/passwd #输出符合条件的所有行
awk -F ":" '$3>15{a++}END{print a}' /etc/passwd #输出最后一个总数
2.6 FIELDWIDTH
设置了 FIELDWIDTH 变量,awk 就会忽略 FS 变量,并根据提供的字段宽度来计算字段,输入3,则代表3个字段放一起,默认空格为分隔符
[root@xiyu_master /root]
#cat date-2.log
2022.03.130000
20220313-18:00
18.00.12
[root@xiyu_master /root]
#awk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' date-2.log
202 2.03. 13 0000
202 20313 -1 8:00
18. 00.12
注意:一旦设定了 FIELDWIDTHS 变量的值,就不能再改变了,因此,这种方法并不适用于变长的字段
三、正则表达式
3.1 表达式符号
^ 开头 /^test/
$ 结尾 /test$/
\* 匹配0个或者多个前导字符
\? 匹配0个1个前导字符
\. 匹配任意单个字符
[] 匹配指定字符组内的任意一个字符
[] 匹配不在指定字符组内的任意一个字符
~ 代表对记录或者字段的表达式进行匹配
~ 代表~取反
3.2 正则实践
目标:查找mysql开头的行
说明://固定格式,以什么开头用^
#cat /etc/passwd |awk '/^mysql/{print $0}'
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false
目标:取第一列以s开头的行
说明:先定义区域,~代表匹配后面条件 //固定格式,以什么开头用^
[root@xiyu_master /root]
#cat /etc/passwd |awk '$1~/^s/{print $0}'
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
目标:匹配最后一个字段为/bin/bash的行
[root@xiyu_master /root]
#awk '$NF~/bin/bash{print $0}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
elsearch:x:1000:1000::/home/elsearch:/bin/bash
www:x:1001:1001::/home/www:/bin/bash
四、案例
4.1 案例一、统计passwd每个单词的重复数量
思路:需要将所有行中按照:进行列排,然后排序,统计
cat /etc/passwd |awk 'BEGIN{RS=":"}{print $0}'|egrep -o [a-zA-Z]+ |sort |uniq -c|sort -rn
或者: RS=" " 内部可以同时使用多个分隔符,如下 / - 空格 :
cat /etc/passwd |awk 'BEGIN{RS="/- :"}{print $0}'|egrep -o "[a-zA-Z]+"|sort -rn |uniq -c
4.2 案例二、过滤IP地址
目标:取IP地址
思路:先将第二排取出来,使用NR==2 ,然后再将行分隔符定为空格,取第二个区域即可
[root@xiyu_master /root]
#ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.19.84.181 netmask 255.255.240.0 broadcast 172.19.95.255
ether 00:16:3e:14:32:7a txqueuelen 1000 (Ethernet)
RX packets 6920718 bytes 1401311001 (1.3 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5003571 bytes 2374606077 (2.2 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@xiyu_master /root]
#ifconfig eth0 |awk 'NR==2&&FS=" " {print $2}'
172.19.84.181