Linux 基础命令(十四)—— 文本处理三剑客之AWK


Linux 基础命令(十四)—— 文本处理三剑客之AWK



 Published On September 06, 2017

   本文部分资料参考自 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会将这段文本中被冒号隔开的字符理解为域,文本中的每一行作为一条记录。 就像下图表现的这样。



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

# 取出当前系统中所有的用户 和密码
# 实际上就是取出 /etc/paswdd   的第一列和第三列

[root@localhost ~]#awk -F: -v  OFS="**" '{print $1,$3}' /etc/passwd
root**0
bin**1
daemon**2
adm**3
lp**4
sync**5
shutdown**6
halt**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/passwd awk –F: '{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/inittab awk 'BEGIN {print ARGC}' /etc/fstab /etc/inittab
ARGV数组,保存的是命令行所给定的各参数awk 'BEGIN {print ARGV[0]}' /etc/fstab /etc/inittab awk '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/passwd
root                 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/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator
games
ftp
nobody

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:root
root or sysuser:bin
root or sysuser:daemon
root or sysuser:adm
......
Common User: basher 
Common User: sh 
Common User: nologin 
Common User: quliang 
root or sysuser:apache
root or sysuser:quagga


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

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

自定义函数

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


function name ( parameter, parameter, ... ) {
    statements
    return expression
}


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


[root@localhost test]#cat f1.txt
function 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值