文本处理三剑客之AWK

时间:2022-06-26 16:07:11

 本文部分资料参考自 http://www.gnu.org/software/gawk/manual/gawk.html 。这是GAWK的官方帮助手册,如果想深入学习的话,可以参考此类文档。

其他参考资料:https://coolshell.cn/articles/9070.html

AWK简介

AWK是一件上古神器,用这句话来形容AWK是最贴切不过了。  纵观计算机发展的历史,我们发现,awk几乎是伴随着计算机操作系统的发展一路走来。1970年作为计算机计时元年,诞生了UNIX和C语言这两种伟大的事物。在这之后,计算机技术飞速发展,1977年贝尔实验室搞出来一款文本处理神器AWK,之所以起名AWK是因为采用了三位创始人Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的名字首字母。想想也是比较有意思的一件事情。  1991年,linus开源了Linux操作系统,至此,计算机进入了迅猛发展的阶段。AWK作为一种神器自然而然地整合到了Linux中,于是作为文本处理三剑客之一的AWK就这样一直使用到了现在。  awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 awk 的 GNU 版本。AWK其实是一种类似编程语言的存在,它集成了编程语言的诸多特性,使用起来灵活多变。本文将简要介绍AWK的使用,力图做到简明扼要。

AWK 基本用法和常见选项

awk 的基本用法一般有下面几种

  • awk [options] ‘program’ var=value file…

  • awk [options] -f programfile var=value file…

  • awk [options] 'BEGIN{ action;… } pattern{ action;… } END{action;… }' file ...

awk 程序通常由:BEGIN语句块、能够使用模式匹配的通用语句块、END语句块,共3部分组成program通常是被单引号或双引号中

awk 常用选项

  • -F :指明输入时用到的字段分隔符

  • -v var=value : 自定义变量

pattern和action

AWK program 的基本格式为 program:pattern{action statements;..} 对于AWK来说,是支持正则表达式(pattern)的,由此可见正则表达式在计算机应用中是多么的重要。

  • pattern部分决定动作语句何时触发及触发事件,如BEGIN,END

  • action statements对数据进行处理,放在{}内指明,如 print printf

分割符、域(也可以理解为列,字段,属性…)和记录(可以理解为一条数据,记录)

这里应该详细的介绍一下AWK的处理机制。AWK 通常被用作报告生成器,格式化文本输出。
就拿我们最熟悉的 /etc/passwd 文件来说,如果我们指定的分隔符为冒号(:),那么AWK会将这段文本中被冒号隔开的字符理解为域,文本中的每一行作为一条记录。 就像下图表现的这样。

文本处理三剑客之AWK

那么 具体来说,到底应该如何使用呢?

# 取出当前系统中所有的用户 和密码# 实际上就是取出 /etc/paswdd   的第一列和第三列[root@localhost ~]#awk -F: -v  OFS="**" '{print $1,$3}' /etc/passwdroot**0bin**1daemon**2adm**3lp**4sync**5shutdown**6halt**7.....

AWK 工作原理

第一步:执行BEGIN{action;… }语句块中的语句 第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{action;… }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。 第三步:当读至输入流末尾时,执行END{action;…}语句块。
BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。
END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块。
pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块。

AWK 变量

AWK 中内置了很多的变量,同时也可以自定变量来使用。下面介绍一下AWK中内置的变量。

变量名称 作用 举例
FS 输入字段分隔符,默认为空白字符

awk -v FS=':' '{print $1,FS,$3}' /etc/passw

dawk �CF: '{print $1,$3,$7}' /etc/passwd

OFS 输出字段分隔符,默认为空白字符 awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd
RS 输入记录分隔符,指定输入时的换行符,原换行符仍有效 awk -v RS=' ' '{print }' /etc/passwd
ORS 输出记录分隔符,输出时用指定符号代替换行符 awk -v RS=' ' -v ORS='###' '{print }' /etc/passwd
NF 字段数量

awk -F: '{print NF}' /etc/fstab

awk -F: '{print $(NF-1)}' /etc/passwd

NR 行号 awk '{print NR}' /etc/fstab ; awk END'{print NR}' /etc/fstab
FNR 各文件分别计数,行号 awk '{print FNR}' /etc/fstab /etc/inittab
FILENAME 当前文件名 awk '{print FILENAME}' /etc/fstab
ARGC 命令行参数的个数

awk '{print ARGC}' /etc/fstab /etc/initta

bawk 'BEGIN {print ARGC}' /etc/fstab /etc/inittab

ARGV 数组,保存的是命令行所给定的各参数

awk 'BEGIN {print ARGV[0]}' /etc/fstab /etc/initta

bawk 'BEGIN {print ARGV[1]}' /etc/fstab /etc/inittab

printf 命令

AWK中有一个与print类似的命令叫做printf。printf能够对处理之后的字符串格式化输出。 在shell 中也有一个这样的内置命令,二者的使用是类似的。同时在使用printf的时候也可以参考C语言的输出方式来理解。

格式化输出:printf “FORMAT”, item1, item2, …
(1) 必须指定FORMAT
(2) 不会自动换行,需要显式给出换行控制符,\n
(3) FORMAT中需要分别为后面每个item指定格式符

格式符:与item一一对应

  • %c: 显示字符的ASCII码

  • %d, %i: 显示十进制整数

  • %e, %E:显示科学计数法数值

  • %f:显示为浮点数

  • %g, %G:以科学计数法或浮点形式显示数值

  • %s:显示字符串

  • %u:无符号整数

  • %%: 显示%自身

修饰符:

  • #[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f

  • -: 左对齐(默认右对齐) %-15s

  • +:显示数值的正负符号 %+

试验一 取出当前系统中所有的用户 和密码

# 取出当前系统中所有的用户 和密码# 实际上就是取出 /etc/paswdd   的第一列和第三列[root@localhost ~]#awk -F: '{printf "%-20s %-10d \n" ,$1,$3}' /etc/passwdroot                 0          bin                  1          daemon               2          adm                  3          lp                   4          sync                 5          shutdown             6    .......

试验二 取出当前系统中硬盘的使用率

# 取出当前系统中硬盘的使用率[root@localhost ~]#df | grep '^/dev/sd'| awk '{printf "Devname:%s Use:%s\n",$1,$5}'Devname:/dev/sda2 Use:22%Devname:/dev/sda3 Use:1%Devname:/dev/sda1 Use:17%

AWK 模式匹配

AWK 支持模式匹配,也就是说,AWK根据pattern条件,过滤匹配的行,再做处理

  • 如果未指定:空模式,匹配每一行

  • /regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来 ,这与sed是相似的。

  • relational expression: 关系表达式,结果为“真”才会被处理

    • 真:结果为非0值,非空字符串

    • 假:结果为空字符串或0值

  • line ranges:行范围 startline,endline:/pat1/,/pat2/ 不支持直接给出数字格式 ,处理匹配这两个模式之间的行的范围。

  • BEGIN/END模式:

    • BEGIN{}: 仅在开始处理文件中的文本之前执行一次

    • END{}:仅在文本处理完成之后执行一次

#打印 root 行到nobody行之间的第一列[root@localhost ~]#awk -F: '/^root\>/,/^nobody\>/{print $1}' /etc/passwdrootbindaemonadmlpsyncshutdownhaltmailoperatorgamesftpnobody

AWK 控制语句

前面我们说过,AWK是一种类似于编程语言的文本处理器。之所以这样说,是因为AWK中支持丰富的控制语句。这些控制语句与C语言很相似,可以结合C语言来理解,在使用上比shell要简单很多。

AWK 中的控制结构举例

  • { statements;… } 组合语句

  • if(condition) {statements;…} if语句

  • if(condition) {statements;…} else {statements;…} if else 语句

  • while(conditon) {statments;…} while 语句

  • do {statements;…} while(condition) do while 循环

  • for(expr1;expr2;expr3) {statements;…} for 循环

  • switch case 语句

  • break 语句

  • continue 语句

  • delete array[index]

  • delete array

  • exit

试验一 /etc/passwd 中UID 小于1000输出系统用户,大于1000 输出普通用户

[root@localhost ~]#awk -F: '{if($3>=1000){printf "Common User: %s \n",$1}else{printf "root or sysuser:%s\n",$1}}' /etc/passwd  root or sysuser:rootroot or sysuser:binroot or sysuser:daemonroot or sysuser:adm......Common User: basher Common User: sh Common User: nologin Common User: quliang root or sysuser:apacheroot or sysuser:quagga

试验二 输出/etc/grub2.cfg 文件中以linux16开头的行中每个字段的长度

[root@localhost ~]#awk '/^[[:space:]]*linux16/{i=1;while(i<NF){print $i,length($i);i++}}' /etc/grub2.cfglinux16 7/vmlinuz-3.10.0-514.el7.x86_64 30root=UUID=0115740e-4b33-4387-bd21-722a969f7fd8 46ro 2crashkernel=auto 16rhgb 4quiet 5linux16 7/vmlinuz-0-rescue-ecc418eb09cb4d05969fbaafae323000 50root=UUID=0115740e-4b33-4387-bd21-722a969f7fd8 46ro 2crashkernel=auto 16rhgb 4quiet 5

AWK 函数

AWK 函数的使用方式与它的控制结构一样,与C语言很类似,可以结合下面的示例好好的理解一下。 
同时AWK本身有一些简单易用的库函数,可以直接在AWK命令中进行使用。

内置函数

AWK 中的内置函数如下表所示。

变量名称 作用
rand() 返回0和1之间一个随机数
length([s]) 返回指定字符串的长度
sub(r,s,[t]) 对t字符串进行搜索r表示的模式匹配的内容,并将第一个匹配的内容替换为s
gsub(r,s,[t]) 对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容
split(s,array,[r]) 以r为分隔符,切割字符串s,并将切割后的结果保存至array所表示的数组中,第一个索引值为1,第二个索引值为2,…

试验一 将8008:08:08 08:08:08 中冒号替换

[root@localhost ~]#echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'2008-08:08 08:08:08[root@localhost ~]#echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'2008-08-08 08-08-08

试验二 统计网络访问中同一IP 的访问次数

[root@localhost ~]#netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'0.0.0.0 6172.18.251.85 1

自定义函数

自定义函数的格式 与C语言的函数格式很相似,并且还具有形式参数和实际参数,返回值也与bash 中有所不同。

function name ( parameter, parameter, ... ) {statementsreturn expression}

我们定义一个函数,用来返回两个值中的最大值

[root@localhost test]#cat f1.txtfunction max ( param1,param2) {    param1 > param2 ? var=param1 :var=param2        return var}BEGIN{print max(a,b)}[root@localhost test]#awk -v a=10 -v b=20 -f f1.txt 20

AWK是十分强大的,通过上面的介绍我们也仅仅是介绍了AWK的一些皮毛而已,在实际生产中还需要根据实际需要查阅相关资料来实现我们的需求才可以。




个人博客地址:http://www.pojun.tech/ 欢迎访问

本文出自 “xiaoshuaigege” 博客,请务必保留此出处http://xiaoshuaigege.blog.51cto.com/6217242/1965111