awk
一、概述
原理
- 逐行读取文本,默认是用空格键或 “ tab ” 键为分隔符进行分隔
- 将分隔所得的各个字段都保存到内建变量中,并按模式或者条件执行编辑命令
- 与sed编辑器类似
特点
-
sed命令通常用于一个整行的处理,awk则是倾向于将一整行分成多个“字段”,然后再进行处理
-
awk信息的读入是逐行读取的,执行结果可以通过 print 的功能将字段打印出来
-
在使用awk命令的过程中,可以用逻辑操作符号表示以下功能
逻辑操作符 | 代表 |
---|---|
&& | 与 |
丨丨 | 或 |
! | 非 |
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
^ | 取余和乘方 |
二、awk命令
2.1 格式
awk 选项 '模式或条件 {操作}' 文件1 文件2
或
awk -f 脚本文件 文件1 文件2
2.2 常见变量
变量名 | 作用 |
---|---|
FS | 列分割符,指定每行文本的字段分隔符,默认是空格或制表位,和 “-F” 一样 |
NE | 当前处理的行的字段个数 |
NR | 当前处理的行的行号(序数) |
$0 | 当前处理的行的整行内容 |
$n | 当前处理行的第n个字段(第n列) |
FILENAME | 被处理的文件名 |
RS | 行分隔符,awk文件读取资料时,根据RS的定义来分割成多条记录,而awk一次仅读入一条记录,以进行处理,预设值是 “ \n ” |
三、测试
3.1 简易文本输出测试
- 输出全部内容
# 功能一样,$0单独使用时可不添加
awk '{print}' cc.txt
awk '{print $0}' cc.txt
A
B
C
D
E
F
G
H
111
222
333
- 输出范围行数
- 共两种方式,功能一样
awk 'NR==1,NR==5 {print}' cc.txt # “==”表示等于,NR为当前行数
awk '(NR>=1)&&(NR<=5) {print}' cc.txt # >=,<= 同样代表一定范围行,中间需要使用&&连接
A
B
C
D
E
- 输入指定行数
- 使用||指定输出所需行数
awk 'NR==1||NR==5 {print}' cc.txt # ||表示或
A
E
- 输出奇偶行
- 当前行取余等于1则是奇数,0则是偶数
awk '(NR%2)==1 {print}' cc.txt # 奇数行
A
C
E
G
111
333
awk '(NR%2)==0 {print}' cc.txt # 偶数行
B
D
F
H
222
- 输出需求开头或者结尾的行
- 与sed一样,^代表开头,$代表结尾
awk '/^4/ {print}' cc.txt # 4开头的行
4D
awk '/Z$/ {print}' cc.txt # Z结尾的行
111Z
3.2 BEGIN
BEGIN 模式表示,在处理指定的文本之前,需要先执行此模式中指定的动作, awk 在处理指定的文本,之后再执行 END 模式中指定的动作,END{ } 语句中,一般都会放打印结果等语句
举列
- 筛选出passwd文件内已 /bin/bash 结尾的行数
- \表示转义,否则 /bin 无法查找到
awk 'BEGIN {x=0};/\/bin\/bash$/{x++};END {print x}' passwd
16
3.3 输出特殊字段
- 输出每行中第一个字段
- 这里需要使用 -F 把文本内每行字段之间的 " : " 指定出来
awk -F ":" '{print $1}' passwd # 打印第一个字段
root
bin
daemon
adm
lp
sync
shutdown
halt
- 输出每行的第1和第3字段
- 和前面一样,加个 $3 即可
awk -F ":" '{print $1,$3}' passwd # 默认输出出来的都是以空格做分隔
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
- 基于上面例题,再增加一条要求第三段数值小于10的行
awk -F ":" '$3<10{print $1,$3}' passwd # 添加个$3<10即可
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
- 再做改进,单独输出第三段数值大于1000的行
- 大于也就是不小于,使用!
- BEGIN命令也可使用,先处理BEGIN,再处理后面的
awk -F ":" '!($3<1000){print $3}' passwd # 使用!别忘了用()将内容合并一起
65534
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
awk 'BEGIN {FS=":"};{if($3>=1000){print}}' passwd # 此命令也可以
- 给每行添加序号
awk -F ":" '{print NR,$0}' passwd # 每处理完一条,NR值加1
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
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
...
57 ccaadd:x:1014:1014::/home/ccaadd:/bin/bash
- 输出 /bash 结尾行的第一段内容
awk -F ":" '$7~"/bash"{print $1}' passwd # 先搜索/bash结尾的行,然后打印出第一段
root
dcc
123456
ccd
ddccdd
dccccccc
ccccc
ccaa
ccaacc
dcc666
ccaz
ssassa
33333
1212313
465456465
ccaadd
- 输出第一段是root开头,而且还需要共七段,并且只输出其中的第1.2段
awk -F ":" '($1~"root")&&(NF==7){print $1,$2}' passwd # 按照顺序搜索
root x
- 输出第七段不是/bin/bash 也不是 /sbin/nologin 的行
awk -F ":" '!($7!="/bin/bash")&&($7!="/sbin/nologin"){print}' passwd # 两个选项放一起使用&&
root:x:0:0:root:/root:/bin/bash
dcc:x:1000:1000:dcc:/home/dcc:/bin/bash
123456:x:1001:1001::/home/123456:/bin/bash
ccd:x:1002:1002::/home/ccd:/bin/bash
ddccdd:x:1003:1003::/home/ddccdd:/bin/bash
...
三元运算符使用
- 例图
- 需求
- 要求输出这两列中字符大的数值
- 如果第三个字段的值大于第四个字段,则把第三字段值赋给max,反之则把第四段值赋给max
awk -F ":" '{max=($3>=$4)?$3:$4;{print max}}' passwd
0
1
2
4
7
5
6
7
12
11
3.4 配合shell命令使用
- 统计PATH中,以 “ : ” 分隔的段落数
echo $PATH | awk 'BEGIN{RS=":"};END{print NR}' # END最后最后执行
5
- 统计 bash 用户的个数
- 配合管道符合,使用 wc -l 命令
awk -F ":" '/bash$/{print | "wc -l"}' passwd
16
- 查看系统内存使用比
- free -m查看系统内存
- 搜索出Mem这一行,然后使用第三段除以第三+四段(总内存)
- 乘100加上% 更直观
free -m | awk '/Mem:/ {print int($3/($3+$4)*100)"%"}'
22%
- 查看CPU的空闲率
- top -b -n 1 查看本机进程信息
- 使用 grep 搜索出 cpu 这一行
- $4 晒出第四段
- $1 输出第一段,并且加上一个% 直观些
top -b -n 1 | grep Cpu | awk -F "," '{print $4}' | awk '{print $1"%"}'
100.0%
- 显示上次系统重启的时间
- second ago 表示显示多少秒前的时间
- %F %H:%M:%S 表示时间格式,年月日秒
date -d "$(awk -F "." '{print $1}' /proc/uptime) second ago" +"%F %H:%M:%S"
2021-05-17 02:48:29
3.5 getline
- 当 getline 左右没有重定向符 “ < ” 或 “ | ” 时,awk就会先读取第一行,然后 geline ,就到第二行。因为 getline 之后,awk 会改变对应的 NF,NR,FNR 和 $0 等内部变量,所以此时的 $0 的值就不是1,而是2了
- 当 getline 左右有重定向符时,getline 则作用于定向输入文件,由于该文件是刚打开,并没有被 awk 读入一行,只是 getline 读入,那么 getline 返回的是该文件的第一行,而不是隔行
举列
- seq 10 输出的是1-10行
- 先由awk获取第一行,然后getline获取第二行,反复进行后,得到的结果如下
seq 10 | awk '{getline;print $0}'
2
4
6
8
10
- 反过来则是,print $0先读取了awk读取的第一行
- getline读取2,但是不打印,反复循环,结果如下
seq 10 | awk '{print $0;getline}'
1
3
5
7
9
3.6 OFS
OFS:输出的分隔符
举列
- OFS表示输出的分隔符为 “ | ”
- $1=$1 用来激活$0的赋值
- 通常会在改变OFS后面需要输出$0时才要求重新激活
echo "A B C D" | awk '{OFS="|";print $0;$1=$1;print $0}'
A B C D
A|B|C|D
3.7 awk与数组
- 设置下标0 1的值,打印出对应结果
awk 'BEGIN{a[0]=10;a[1]=20; print a[1]}' # 输出下标1
20
awk 'BEGIN{a[0]=10;a[1]=20; print a[0]}' # 输出下标2
10
- 同样设置,将下标改为英文字母
awk 'BEGIN{a["abc"]=10;a["def"]=20; print a["abc"]}' # 英文字母需要使用 “”
10
awk 'BEGIN{a["abc"]=10;a["def"]=20; print a["def"]}'
20
- 设置循环打印
awk 'BEGIN{a[0]=10;a[1]=20;a[2]=30;for(i in a){print i,a[i]}}'
0 10
1 20
2 30
BEGIN中的命令只能执行一次
awk数组的下标除了数字还可以使用字符串,不过字符串需要 “”