AWK

38 阅读5分钟

工作原理

  • 逐行读取文本默认以空格或tab键为分隔符进行分隔,将分隔所得的各个字段保存到内建变量中,并按模式或者条件执行编辑命令。
  • sed命令常用于一整行的处理,而awk比较倾向于将一行分成多个"字段"然后再进行处理。awk信息的读入也是逐行读取的,执行结果可以通过print的功能将字段数据打印显示。在使用awk命令的过程中,可以使用逻辑操作符"&&"表示"与"、"||"表示"或"、"!" 表示"非":还可以进行简单的数学运算,如+、-、*、/、%、^分别表示加、减、乘、除、取余和乘方。

AWK 工作流程可分为三个部分:

  • 读输入文件之前执行的代码段(由BEGIN关键字标识)。

  • 主循环执行输入文件的代码段。

  • 读输入文件之后的代码段(由END关键字标识)。

  • 1、通过关键字 BEGIN 执行 BEGIN 块的内容,即 BEGIN 后花括号 {} 的内容。

  • 2、完成 BEGIN 块的执行,开始执行body块。

  • 3、读入有 \n 换行符分割的记录。

  • 4、将记录按指定的域分隔符划分域,填充域,0 则表示所有域(即一行内容),0 则表示所有域(即一行内容),0 则表示所有域(即一行内容),1 表示第一个域,$n 表示第 n 个域。

  • 5、依次执行各 BODY 块,pattern 部分匹配该行内容成功后,才会执行 awk-commands 的内容。

  • 6、循环读取并执行各行直到文件结束,完成body块执行。

  • 7、开始 END 块执行,END 块可以输出最终结果。

命令格式

erlang
复制代码
awk 选项 '模式或条件{操作}′ 文件1 文件2 ...
awk -f  脚本文件  文件1  文件2 ...

awk常见的内建变量(可直接用)如下所示:

FS:列分割符。指定每行文本的字段分隔符,默认为空格或制表位。与"-F"作用相同

NF:当前处理的行的字段个数

NR:当前处理的行的行号(序数)

$o:当前处理的行的整行内容

$n:当前处理行的第n个字段(第n列)

FILENAME:被处理的文件名

RS:行分隔符。awk从文件上读取资料时,将根据s的定义把资料切割成许多条记录,而awk一次仅读入一条记录,以进行处理。预设值是'\n'

$0

  1. 在shell主代码中代表的是脚本名
  2. 在函数体重代表的是函数名
  3. 在awk的命令中代表的是当前整行内容

按行输出文本

不同位置的$1的含义

#!/bin/bash

myfunc() {
   awk '{peint $1}'$1 #括号里面的$1代表输出当前行的第一个字段
                      #外面的$1调用函数时后面根的位置变量
}

#### main ###
myfunc $1 #执行脚本时第一个位置变量

显示全部内容

[root@localhost ~]# awk '{print}' 3.txt
one
two
three
four
five
six
seven
eigh
nine
ten
[root@localhost ~]# awk '{print $0}' 3.txt
one
two
three
four
five
six
seven
eigh
nine
ten

双引号也可以输出全文

[root@localhost mm]# awk "{print}" 3.txt
one
two
three
four
five
six
seven
eigh
nine
ten
[root@localhost ~]# awk "{print $0}" 3.txt 
//加了双引号以后$就有特殊意义了,AWK将无法识别$符号
0
0
0
0
0
0
0
0
0
0
[root@localhost ~]# awk "{print$0}" 3.txt
//一定要用双引号的话可以使用\号转译即可
one
two
three
four
five
six
seven
eigh
nine
ten

输出一到三行的内容

NR命令可以输出行

[root@localhost ~]# awk 'NR==1,NR==3 {print $0}' 3.txt
one
two
three

输出第四行到第八行的两种方法

[root@localhost ~]# awk 'NR==4,NR==8{print $0}' 3.txt
four
five
six
seven
eigh
[root@localhost ~]# awk '(NR>=4)&&(NR<=8) {print $0}' 3.txt
four
five
six
seven
eigh

输出四和八行

scss
复制代码
[root@localhost ~]# awk '(NR==4)||(NR==8) {print $0}' 3.txt
four
eigh

输出奇数行与偶数行

[root@localhost ~]# awk '(NR%2)==1 {print $0}' 3.txt
one
three
five
seven
nine

[root@localhost ~]# awk '(NR%2)==0 {print $0}' 3.txt
two
four
six
eigh
ten

awk可以使用//来找行里面的内容(也可以配合正则表达式)

[root@localhost ~]# awk '/root/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@localhost ~]# awk '/^root/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@localhost ~]# awk '/bash$/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
w:x:1000:1000:w:/home/w:/bin/bash
wangwu:x:1001:1001::/home/wangwu:/bin/bash
awk 'BEGIN{操作1}; [条件]{操作2}; END{操作3}'  文件x

模式
BEGIN{操作1}    awk在读取文件之前执行的操作
[条件]{操作2}    awki逐行读取文件时执行的操作
END {操作3 }     awk在处理完文件所有行之后执行的操作

awk 'BEGIN {x=0} ;//bin/bash$/{x++};END {print x} ' /etc/passwd
awk 'BEGIN {x=0}  //AWK在执行命令之前会给/etc/passwd赋一个值给X
//bin/bash$/{x++}  //AWK根据条件去寻找行,会转译bin bash的两个斜杠所在的行  每个包含bin bash的话加1
END {print x}  //输出x的值

统计以nologin结尾的行

[root@localhost ~]# grep -c "nologin$" /etc/passwd
39
[root@localhost ~]# awk 'BEGIN {a=0}; /nologin$/ {a++}; END {print a}' /etc/passwd
39

[root@localhost ~]# awk 'BEGIN {a=0}; /nologin$/ {print a;a++}; END {print "共有"a"行"}' /etc/passwd
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
共有39

按字段输出文本

列出状态列

[root@localhost ~]# ps -elf | awk '{print $2}'
S
S
S
S
S
S

过滤空闲的内存空间

[root@localhost ~]# free | awk '{print $3}'
free
783220
0

[root@localhost ~]# free |grep Mem: |awk '{print $3}'
783468

[root@localhost ~]# free |awk 'NR==2 {print $3}'
783356

[root@localhost ~]# free | awk '/Mem:/ {print $3}'  //字符串也可以查找
782968

找到本机的IP地址与MAC地址

[root@localhost ~]# ifconfig | awk 'NR==2 {print $2,$4}'
192.168.85.10 255.255.255.0
[root@localhost ~]# ifconfig  ens33 | awk '/RX p/ {print $5}'  //入站流量
1410280
[root@localhost ~]# ifconfig  ens33 | awk '/TX p/ {print $5}'  //出站流量
813112

加单位输出
[root@localhost ~]# ifconfig  ens33 | awk '/TX p/ {print $5,$4}'
821272 bytes

-F 指定分隔符

[root@localhost ~]# awk '{print $1,$3}' /etc/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
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown


[root@localhost ~]# awk -F ':' '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
operator 11

查看UDI号大于500的

[root@localhost ~]# awk -F ':' '$3>=500 {print $1,$3}' /etc/passwd
polkitd 999
libstoragemgmt 998
colord 997
saslauth 996
setroubleshoot 995
chrony 994
geoclue 993
sssd 992
nfsnobody 65534
gnome-initial-setup 991

[root@localhost ~]# awk -F ':' '!($3<=500) {print $1,$3}' /etc/passwd
//!号取反也可以实现
polkitd 999
libstoragemgmt 998
colord 997
saslauth 996
setroubleshoot 995
chrony 994
geoclue 993
sssd 992
nfsnobody 65534
gnome-initial-setup 991
w 1000
wangwu 1001

AWK模式

[root@localhost ~]# awk 'BEGIN{FS=":"}; {print $1,$3}' /etc/passwd
//使用BEGIN这个模式后,系统在处理文件之前,会先把FS的值改变,再去处理print的操作
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7

在使用if语句的时候需要主意两边都要加上{}

[root@localhost ~]# awk -F ':' '{if($3<=10) {print $1,$3}}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8

UID≥JID 输出UID号 UID<JID 输出JID号

[root@localhost ~]# awk -F ':' '{max=($3>=$4)?$3:$4; {print $1,max}}' /etc/passwd
root 0
bin 1
daemon 2
adm 4
lp 7
sync 5
shutdown 6
halt 7
mail 12

输出行号与内容

[root@localhost ~]# sed -n '=;p' 3.txt
1
one
2
two
3
three
4
four
5
five
6
six
7
seven
8
eigh
9
nine
10
ten

[root@localhost ~]# awk '{print NR,$0}' 3.txt
1 one
2 two
3 three
4 four
5 five
6 six
7 seven
8 eigh
9 nine
10 ten

包含o的行 ~包含的意思

[root@localhost ~]# awk '$1~"o"{print NR,$0}' 3.txt
1 one
2 two
4 four

通过管道,双引号调用shell命令

统计以冒号分隔的文本段落数,END{ }语句块中,往往会放入打印结果等语句

[root@localhost ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost ~]# echo $PATH | awk 'BEGIN{RS=":"}; {print NR,$0} ;END{print NR}'
1 /usr/local/sbin
2 /usr/local/bin
3 /usr/sbin
4 /usr/bin
5 /root/bin

查找已有的用户个数

[root@localhost ~]# grep -c "bash$" /etc/passwd
34

[root@localhost ~]# awk '/bash$/ {print $0}' /etc/passwd |wc -l
34

[root@localhost ~]# awk '/bash$/ {print $0 | "wc -l"}' /etc/passwd
34

算出当前内存的空闲率

[root@localhost ~]# free |awk '/Mem:/ {print $3/$2*100}' | awk -F. '{print $1"%"}'
8%

[root@localhost ~]# free |awk '/Mem:/ {print int($3/$2*100)"%"}'
8%
[root@localhost ~]# top -b -n1 | awk '/Cpu/{print $8}' 
但值不为100时就会出现问题
96.9
[root@localhost ~]# top -b -n1 | awk '/Cpu/{print $8}'
id,

[root@localhost ~]# top -b -n1 | awk -F , '/Cpu/{print $4}'
100.0 id

[root@localhost ~]# top -b -n1 | awk -F , '/Cpu/{print $4}' | awk '{print $1}'
96.9
[root@localhost ~]# top -b -n1 | awk -F , '/Cpu/{print $4}' | awk '{print $1}'
100.0
[root@localhost ~]# top -b -n1 | awk -F , '/Cpu/{print $4}' | awk '{print $1"%"}'
100.0%

想查看服务器是否有重启过(查看上一次开机时间)

[root@localhost ~]# cat /proc/uptime
179.17 //系统的启动时间单位(秒)    331.86  //系统的空闲时间但是(秒)

[root@localhost ~]# date -d "179.17 second ago" +"%Y%m%d %H:%M:%S"
20220912 10:23:53

[root@localhost ~]# date -d "$(awk '{print $1}' /proc/uptime) second ago" +"%Y%m%d %H:%M:%S"
20220912 10:18:43

奇数,偶数

[root@localhost ~]# seq 10 | awk '{getline; print $0}'
2
4
6
8
10

[root@localhost ~]# seq 10 | awk '{print $0; getline }'
1
3
5
7
9
  • 当getline左右无重定向符"<"或"|"时,awk首先读取到了第一行,就是1,然后getline,就得到了1下面的第二行,就是2,因为yetline之后,awk会改变对应的NF,NR,FNR和o等内部变量,所以此时的o等内部变量,所以此时的o等内部变量,所以此时的o的值就不再是1,而是2了,然后将它打印出来。
  • 当getline左右有重定向符"<"或"|"时,getline则作用于定向输入文件,由于该文件是刚打开,并没有被awk读入一行,只是getline读入,那么getline返回的是该立件的第—行.而不是隔行。

系统名称

[root@localhost ~]# awk 'BEGIN {"hostname" | getline; {print $0}}'
localhost.localdomain

[root@localhost ~]# w | awk 'NR>=3 {print $0 | "wc -l"}'
1


[root@localhost ~]# awk 'BEGIN{n=0; while("w" | getline) n++; print n-2}'
1

总结

awk 'BEGIN{操作1}; [条件]{操作2}; END{操作3}'  文件x

模式
BEGIN{操作1}    awk在读取文件之前执行的操作
[条件]{操作2}    awki逐行读取文件时执行的操作
END {操作3 }     awk在处理完文件所有行之后执行的操作
cpu负载  uptime    w    top
cpu使用率  top   vmstat
内存   free  top  vmstat
硬盘空间  df
网卡流量   ifconfig
安装的软件包数量   rpm -qa l wc -l
账户数量   cat letc/passwd l wc -l
登录账户   who wusers
进程数量  ps aux l wc -l
ssh登录  /var/ log/ secure