一、awk介绍

  awk是文本三剑客之一,其实awk是一种语言,该语言的创始者定义为”生成报表和格式化文本输出“awk有很多种版本,这里介绍的是GUN awk(gawk)

二、awk工作原理

  第一步:执行BEGIN{action}语句块中的语句,该语句块不依赖于文件,awk在执行是,将在读取文件之前执行该语句中的语句块,常用语变量的初始化,打印输出表格的表头。
  第二步:从文件、标准输入、上一条命令输出结果输入地区一行,然后进行pattern{aciton}语句块,它将逐行扫描文件,从第一行到最后一行。若没有提供pattern语句,则默认执行打印{print},即打印每一个读取到的行。
  第三步:当读至文件最后时,执行END{action}语句块。通常用于汇总在pattern语句中执行的过程

三、awk基本用法

选项
-F"指定分隔符"
-v 自定义变量:定义变量
      
awk {print} file
awk将把file文件中的每一行都读取一遍,然后输出在终端上
输入内置变量在文本中代表如下图所示
awk用法详解

[root@centos6 ~]#awk -F: -v OFS="===" -v  ORS="####" '{print $1,$2}' /etc/passwd  #将输出间隔符换为===将换行符换为###输出结果如下
root===x####bin===x####daemon===x####adm===x####lp===x####sync===x####shutdown===x####halt===x####mail===x####uucp===x####operator===x####games===x####gopher===x####ftp===x####nobody===x####vcsa===x####saslauth===x####postfix===x####sshd===x####lin===x####tcpdump===x####hacker===x####dbus===x####test===x####apache===x####[root@centos6 ~]#  

awk用法详解
实例一

[root@centos6 ~]#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 uucp 10 operator 11

四、awk内置变量

  awk命令的print打印内容也可以不与文件有关,若没有关系,则表示文件有多少行内容,就会打印自己所指定的内容,而在awk中,处理动作中的字母若不用" "引上则表示使用变量,所以若需要输出字符串则需要用“ ”引上。数字则不需要。
awk用法详解
实例一、输入分隔符

[root@centos6 ~]#echo {1..10}
1 2 3 4 5 6 7 8 9 10 [root@centos6 ~]#echo {1..10}|awk -v FS=" " '{print $1,$3}' 1 3

  这里表示原本输出结果为1到时,拿FS内置变量举例,设置空白字符为分隔符(默认分隔符就是空白字符,所以不指定结果也是一样的,这里只是为了举例说明),取1,3列,
实例二、替换输出分隔符

[root@centos6 ~]#echo {1..10} | awk -v OFS=":" '{print $1,$2,$3}'
1:2:3

  这里OFS内置变量表示将输出结果分隔符变成:,取1,2,3列
实例三、分别显示两个文本的行号

[root@centos6 ~]#awk '{print FNR,$0}' /etc/fstab /etc/issue  #表示分别显示每一个文件的行号
1 
2 #
3 # /etc/fstab 4 # Created by anaconda on Fri Mar 9 08:50:54 2018 5 # 6 # Accessible filesystems, by reference, are maintained under '/dev/disk' 7 # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info 8 # 9 /dev/mapper/vgentos6-LogVol03 /                       ext4   defaults        1 1 10 /dev/mapper/vgentos6-LogVol02 /app                   ext4   defaults        1 2 11 UUID=8f86cc7e-f593-467d-b823-eae6610616a1 /boot                   ext4   defaults        1 2 12 /dev/mapper/vgentos6-LogVol00 /var                   ext4   defaults        1 2 13 /dev/mapper/vgentos6-LogVol01 swap                   swap   defaults        0 0 14 tmpfs                   /dev/shm               tmpfs   defaults        0 0 15 devpts                 /dev/pts               devpts  gid=5,mode=620  0 0 16 sysfs                   /sys                   sysfs   defaults        0 0 17 proc                   /proc                   proc   defaults        0 0 18 /dev/cdrom /mnt/base auto defaults 0 0 1 CentOS release 6.9 (Final) 2 Kernel \r on an \m 3 

五、printf格式化输出

  在输出内容时,print输出只能输出规定格式的内容,不能自定义格式,所以想要让输出结果根据自己所需要的格式输出就需要用printf来定制格式
printf使用格式
awk ‘{printf "格式1 格式 ",$1,$2}’,格式必须和需要输出的列一一对应
awk用法详解
实例一、格式化输出,让文本左对齐

[root@centos6 ~]#awk -F: '{printf "%-20s %-20d\n",$1,$3}' /etc/passwd
root                 0                   
bin                  1                   
daemon               2                   
adm                  3                   lp                   4                   sync                 5                   shutdown             6                   halt                 7                   mail                 8                   uucp                 10           

        因为printf不会自动换行,所以在规定好的格式后面增加\n来起到换行符的作用,其中-代表左对齐,若需要右对齐则直接填整数即可

六、awk运算符

  awk也可以进行数字间的运算,不但支持整数,而且支持小数
awk用法详解
实例一、两个数之间的运算

[root@centos6 ~]#awk 'BEGIN{print 5+10}'
15
[root@centos6 ~]#awk 'BEGIN{print 5.5+10.5}'
16
[root@centos6 ~]#awk 'BEGIN{print 40/3}' 13.3333

七、赋值运算

在赋值操作符中,sum+=i,就相当于sum=sum+i,
awk用法详解
实例一、将100以内的数相加
seq 100 | awk '{sum+=$1}END{print sum}'
5050
  因为awk本来就是针对行行的循环,可以根据这个特性将1到100相加在END部分将sum值输出,就实现了100百以内数相加
实例二、变量的自增

[root@centos6 ~]#awk '{print i++}' /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

       在awk中,变量一旦与运算符号结合使用,将认为该变量的初始值为0,这个这条语句表示从零开始/etc/passwd文件有多少行,i变量将自增几次

八、模式匹配

  awk模式通配符~,表示左边的内容是否和右边内容匹配包含,!~则表示不匹配
实例一、在文件中查找包含某字符的行

[root@centos6 ~]#awk -F: '$0 ~ /root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin

  查看passwd这个文件包含root的行,并将其打印到终端
实例二、查找passwd文件中所有/bin/bash的行

[root@centos6 ~]#awk -F: '$0 ~ "/bin/bash"' /etc/passwd
root:x:0:0:root:/root:/bin/bash lin:x:500:500::/home/lin:/bin/bash hacker:x:501:501::/home/hacker:/bin/bash test:x:502:502::/home/test:/bin/bash

  因所搜内容中也包含 ” / “ 所以需要将搜索内容用双引号引起来

九、比较操作符

awk用法详解

十、逻辑操作符

&&:表示同时满足两个条件
||:表示满足两个条件中的一个即可
!:表示取匹配结果的反值
实例一、取同时满足两个条件的结果&&

[root@centos6 ~]#awk -F: '$3>=50 && $3<=100{print $1,$3}' /etc/passwd
nobody 99
vcsa 69
postfix 89
sshd 74 tcpdump 72 dbus 81

实例二、去满足一个或两个条件的结果 ||

[root@centos6 ~]#awk -F: '$3>=50 || $3<=100{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3 lp 4 sync 5 shutdown 6 halt 7 mail 8 uucp 10 operator 11 games 12 gopher 13 ftp 14 nobody 99 vcsa 69 saslauth 499 postfix 89 sshd 74 lin 500 tcpdump 72 hacker 501 dbus 81 test 502 apache 48

十一、条件表达式(三目表达式)

  常用于只有两种情况下的判断,如果情况过多,将不适合使用三目表达式来选择
语法:判断条件?条件为真时执行语句:条件为假时执行语句
实例一、将系统中所有用户根据UID统计是系统用户还是普通用户                       

[root@centos6 ~]#awk -F: '{$3>=500?usertype="common user":usertype="system user";print usertype,$1}' /etc/passwd
#根据UID判断当前系统中所有用户是系统用户还是普通用户
system user root
system user bin
system user daemon system user adm system user lp system user sync system user shutdown system user halt system user mail system user uucp system user operator system user games system user gopher system user ftp system user nobody system user vcsa system user saslauth system user postfix system user sshd common user lin system user tcpdump common user hacker system user dbus common user test system user apache

十二、PATTERN模式搜索

  该模式下也支持扩展正则表达式,支持模糊搜索
1.如果未指定,空模式,匹配每一行,默认搜索每一行
实例一

[root@centos6 ~]#awk '{print $0}' /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

2./指定匹配字符/:仅处理能够模式匹配到的行需要用 /  / 括起来,若匹配的行关键字中也有/则需要使用\/来转译
实例一、只有满足root为行首的行才会被匹配

[root@centos6 ~]#awk '/^root/{print $0}' /etc/passwd  #表示只匹配root为行首的行。
root:x:0:0:root:/root:/bin/bash

实例二、只要满足r..o条件的将全部匹配

[root@centos6 ~]#awk '/r..t/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

3.关系表达式,若果为真才会被处理,
真:结果为非0值或非空值
假:结果为空字符串或0值
实例一、若条件为0或者空值时,则不会输出任何结果

[root@centos6 ~]#awk '0{print}' /etc/passwd

实例二、若条件为非0或控制时,则打印结果

[root@centos6 ~]#awk '1{print}' /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

实例三、该表达式中,因为awk自带行间的循环,又因为非空为1,当第一次非空则将赋值1给i则有结果输出,第二次则将一个非1则为0,将0赋给i则第二个不输出,依次类推则输出结果如下

[root@centos6 ~]#seq 10 | awk 'i=!i'
1
3
5
7 9

4.行范围,awk也能取匹配字符的行范围 /匹配字符1/,/匹配字符2/处理动作

[root@centos6 ~]# awk '/^h/,/^f/' /etc/passwd
halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin gopher:x:13:30:gopher:/var/gopher:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin hacker:x:501:501::/home/hacker:/bin/bash dbus:x:81:81:System message bus:/:/sbin/nologin test:x:502:502::/home/test:/bin/bash apache:x:48:48:Apache:/var/www:/sbin/nologin

        当遇到以h为行首的行,则打印,到行首为f结束,因为打印完之后有碰到h为行首,则继续打印,因为后面没有h为行首,则一直打印到就结束

5.BEGIN/END模式
BEGIN{}:仅在开始处理文本之前执行一次
BEGIN属于在文本执行前执行的语句块,它不依赖于任何文件或输出结果

[root@centos6 ~]#awk 'BEGIN{print "test"}'
test

END{}:仅在文本处理完成之后执行一次
END工作在awk处理完文本之后执行一次。可以用于打印pattern语句块执行过程的结果
它也依赖于文件或输出结果

[root@centos6 ~]#awk 'END{print  "test"}' /etc/passwd
test

十三、awk控制语句

1.if else
语法

if(判断条件){满足条件执行的语句} [else {不满足条件执行语句}
if(判断条件1){满足条件1执行语句}else if (判断条件2{满足条件2时执行语句}else{不 满足上述两个条件执行语句]

实例一、根据UID判断系统中用户是系统用户还是普通用户

[root@centos6 ~]#awk -F: '{if($3<=200){name="system"}else {name="user"} print $1,name}' /etc/passwd
root system
bin system
daemon system
adm system lp system sync system shutdown system halt system mail system uucp system operator system games system gopher system ftp system nobody system vcsa system saslauth user postfix system sshd system lin user tcpdump system hacker user dbus system test user apache system

实例二、将20到30的数相加

[root@centos6 ~]#seq 50 |awk '{if($1>=20&&$1<=30){sum+=$1}}END{print sum}'
275

   判断系统中小于300的全部都是系统用户,其他的全部为普通用户
2.while循环
语法
{while (判断条件){循环语句}},条件为真时,开始执行循环
  awk属于行之间的循环,他不会对列进行循环,所以若需要对列进行计算和格式化处理,则需要使用awk内置循环来进行
实例一、将每一行的和输出值终端

[root@centos6 ~]#cat test.txt  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@centos6 ~]#awk '{sum=0;i=1;while(i<=NF){sum+=$i ;i++}print sum}' test.txt 55 155

实例二、统计/boot/grub2/grub.conf文件中行首为linux16行每一个字段的字符数

[root@centos7 ~]# awk '/^[[:space:]]+linux/{i=1;while(i<=NF){print $i,length($i);i++}}' /boot/grub2/grub.cfg 
linux16 7
/vmlinuz-0-rescue-e8e8f687a3b24f13bda5566ae49855e6 50
root=UUID=7b231a1b-a730-40dd-ae89-4d565d07b340 46
ro 2
rhgb 4
quiet 5
net.ifnames=0 13
linux16 7
/vmlinuz-4.15.13.test 21
root=UUID=7b231a1b-a730-40dd-ae89-4d565d07b340 46
ro 2
rhgb 4
quiet 5
net.ifnames=0 13
LANG=en_US.UTF-8 16

        i作为自增项,循环条件为i<=字段数,length($i)内置函数,用于测量字段的长度 
3.do while
语法
do{循环语句}while(判断条件),该语句与while唯一不同就是循环一定执行一次,不管判断条件是否为真
4.for循环
语法
for(变量;判断;变量自增){循环语句},其中()中必须是三部分组成
实例一、该实例和while实现的功能一直,只是写法不同

[root@centos7 ~]# awk '/^[[:space:]]+linux16/{for(i=1;i<=NF;i++){print $i,length($i)}}' /boot/grub2/grub.cfg 
linux16 7
/vmlinuz-0-rescue-e8e8f687a3b24f13bda5566ae49855e6 50
root=UUID=7b231a1b-a730-40dd-ae89-4d565d07b340 46
ro 2
rhgb 4
quiet 5
net.ifnames=0 13
linux16 7
/vmlinuz-4.15.13.test 21
root=UUID=7b231a1b-a730-40dd-ae89-4d565d07b340 46
ro 2
rhgb 4
quiet 5
net.ifnames=0 13
LANG=en_US.UTF-8 16

十三、循环控制语句

break:直接跳出循环

[root@centos6 ~]#cat test.txt 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@centos6 ~]#awk '{for(i=1;i<=NF;i++){if($i%2==0){break}print $i}}' test.txt 1 11

  因为当前文件中有两行内容,则显示两个数,如果只有一行内容,则只显示第一个数,因为当执行到2的时候跳出循环进入下次循环,而11以后则是下次循环,所以还会继续执行
continue:退出这次循环,进入下次循环、

[root@centos6 ~]#cat test.txt 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@centos6 ~]#awk '{for(i=1;i<=NF;i++){if($i%2==0){continue}print $i}}' test.txt 1 3 5 7 9 11 13 15 17 19

当$i的值除以2 余数为0则跳出循环,其他则输出
next:跳出当前行循环,进入下次行循环

[root@centos6 ~]#seq 10 |awk '{if($1%2==0){next}print $1}'
1
3
5
7 9

next控制的是行间的调过,当该行满足这个条件,则跳出这行循环,进入下次循环

十三、awk数组

awk中的数组只有关联数组
1.可以使用任意字符串,字符串要使用双引号括起来
2.如果某数组元素实现不存在,在引用时,awk会自动创建次元素,并将其值初始化为空字符串
语法
数组名["下标"]="参数"
数组可以用于统计某个字符串出现过几次,用来统计次数
实例一、统计access_log文件每一个ip的访问次数

[root@centos6 ~]#awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' access_log 
172.18.250.183 81 172.18.251.149 158 172.18.0.223 12 172.18.251.21 81 172.18.251.122 52 172.18.254.6 1643 172.18.253.21 74087 172.18.251.150 34 172.18.252.134 514

十四、awk内置函数

length():返回指定字符串的长度

[root@centos6 ~]#echo abcde | awk '{print length($1)}'
5

rand():返回0和1之间的随机数,也就是小数在使用rand()函数时,在前面必须指定种子srand(),若想随机整数,则需要乘以一个数(根据想要的位数)
[root@centos6 ~]#awk 'BEGIN{srand();print int(rand()*10)}'
int()也是函数,它的作用是取整数,当前命令会随机取10以内的整数        
sub(r,s,[t]):对t字符串进行搜索r表示模式匹配的内容,并将第一个匹配的内容替换为s
gsub(r,s,[t]):对匹配的字符全局替换

[root@centos6 ~]#echo {1..10} | awk 'gsub(" ",":",$0)'
1:2:3:4:5:6:7:8:9:10

将空格全局替换成:
split(s,array,[r]):以r为分隔符,切割字符串s,并且将切割后的结果保存到array的数组中,第一个索引值为1

[root@centos6 ~]#awk '{split($5,c,":")};{ip[c[1]]++}END{for (i in ip){print $1,ip[i]}}' netstat.log 
tcp 13
tcp 3
tcp 1
tcp 1 tcp 1 tcp 1 tcp 1 tcp 1 tcp 1 tcp 1 tcp 1 tcp 1 tcp 1 tcp 1 tcp 15

十五、awk自定义函数,脚本调用

function name (虚变量1,虚变量2){
                    处理动作
                    处理动作
}

  虚变量指的是需要用户输入两个变量,但是传入函数执行的变量和函数本身变量名不一致,也可以理解成一种定义格式,必须指定两个变量才可以调用该函数
awk程序也可以写成脚本,然后当程序需要是,可以直接调用执行,awk脚本也是需要执行权限,所以在执行之前需要个执行权限
脚本格式

#!/bin/awk -f
脚本内容

实例一

#!/bin/awk -f
function biji (x,y){
        if(x>y){ max="x>y"}
        else if (x==y){max="x=y"}        else{ max="x<y" }       return max } BEGIN{print biji(a,b)} [root@centos6 ~]#awk -v a=7 -v b=7 -f test.awk x=y [root@centos6 ~]#awk -v a=7 -v b=6 -f test.awk x>y [root@centos6 ~]#awk -v a=5 -v b=6 -f test.awk x<y

十六、system命令

  在awk语句中,也可以根据需求调用系统的一些命令,但是要结合system内置函数来实现
语法
awk BEGIN‘system("系统命令")’