Shell awk
1. awk概述
awk不同于grep的文本与sed工具的文本处理,它更偏向于对文本的格式化处理,它不仅仅是一款工具,也是一门解释性语言,其名字来源于它的三位作者的姓氏:Alfred Aho, Peter Weinberger 和 Brian Kernighan,在文本处理非常强大,是一款Linux服务器文本报告器和格式化文本工具。
我们日常工作中有很多需要格式化打印的需求,更多的是关注列操作时,就可以利用awk工具来进行处理。awk除了是工具也同样是一门语言,其允许创建简短的程序来处理自己的需求,这些程序读取输入、为数据排序、处理数据、对输入执行计算以及报表等。非常的强大,相信在掌握了awk,日常运维工作更加方便高效简单。
2. awk的适用场景
超大处理;
格式化的文本报表;
执行算数运算;
执行字符串操作等。
3. awk的处理模式
一般是遍历中的每一行,然后分别对的每一行进行处理。
awk对输入的一行数据进行处理的模式,对整个进行重复执行此模式处理,在此说明对输入的一行数据处理的内在机制如下图所示:
处理过程不断重复,直到到达结尾。
首先读入流的一行到模式空间;
在模式空间内,对进行模式匹配处理;
然后处理后的数据;
清空当前模式空间;
读取第二行输入流到模式空间;
又开始对模式空间内的第二行输入数据进行处理。
总体可以分为以下三步:
读(Read):AWK 从输入流(、管道或者标准输入)中读入一行然后将其存入内存中。
执行(Execute):对于每一行输入,所有的 AWK 命令按顺序执行。 认情况下,AWK 命令是针对于每一行输入,但是我们可以将其限制在指定的模式中。
重复(Repeate):一直重复上述两个过程直到结束。
4. 语法及结构
Awk 语法格式如下图所示:
awk [options] 'PATTERN {action}' file1,file2
awk 的语法格式主要分为四个字段,options 选项,引号内有模块与动作,以及要处理的,接下来让我们详细讲解每语法字段,更全面地认识 awk 这个脚本利器。
awk 在引号内有一定的程序结构,主要为以下:
开始块(BEGIN BLOCK):
语法: BEGIN{awk-commands} 开始块就是awk程序启动时执行的部分(在处理输入流之前执行),并且在整个过程中只执行一次; 一般情况下,我们在开始块中初始化一些变量。BEGIN是awk的关键字,因此必须要大写。【注:开始块部分是可选,即你的awk程序可以没有开始块部分】
主体块(Body Block):
语法: /pattern/{awk-commands} 针对每输入的行都会执行一次主体部分的命令,认情况下,对于输入的每一行,awk都会执行主体部分的命令,但是我们可以使用/pattern/限制其在指定模式下。
结束块(END BLOCK):
语法: END{awk-commands} 结束块是awk程序结束时执行的(在处理完输入流之),END也是awk的关键字,必须大写,与开始块类似,结束块也是可选的。
awk print,例如:
print item1,item2...
1.各字段之间逗号隔开,时以空白字符分隔;
2.的字段可以为字符串或数值,当前记录的字段(如$1)、变量或 awk 的表达式;数值先会转换成字符串然后;
3.print 命令后面的 item 可以省略,此时其相当于print $0
,如果想空白,可以使用print ""
;
例如:
[root@master ~]# awk -F: '{print $1,$NF}' /etc/passwd|column -troot /bin/bash bin /sbin/nologin daemon /sbin/nologin adm /sbin/nologin lp /sbin/nologin sync /bin/sync
awk printf
printf 命令的使用格式:
printf <format> item1,item2...
要点:
1.其与 print 命令最大区别,printf 需要指定 format,format 必须给出;
2.format 用于指定后面的每个 item 格式;
3.printf 语句不会打印换行字符\n
。
format 格式的指示符都以 % 开头,后跟字符:
%:ascall码%:%i:十进制整数%,%E:科学计数法%:浮点数:字符串%u:无符号整数%%:%自身 修饰符:#[.#]:第#控制的宽度:第二个#表示小数点后的精度:%-:左对齐+:数组符号
例如:
[root@master ~]# awk -F: '{printf "Username:%-15s ,Uid:%d\n",$1,$3}' /etc/passwdUsername:root ,Uid: Username:bin ,Uid: Username:daemon ,Uid: Username:adm ,Uid: Username:lp ,Uid: Username:sync ,Uid: Username:shutdown ,Uid:
记录变量:
IFS(input field separator),输入字段分隔符(认空白)
OFS(output field separator),字段分隔符
RS(Record separator):输入文本换行符(认回车)
ORS:文本换行符
数据变量
{print NF},{print $NF}
awk 'BEGIN{print ENVIRON["PATH"]}'
ARGV:数组,保存命令行本身这个字符串
ARGC:awk 命令的参数个数
FILENAME:awk 命令处理的
ENVIRON:当前 shell 环境变量及其值的关联数组
NR:the number of input records,awk 命令所处理的的行数,如果有多个,这个数目会将处理的多个计数
NF:number of field,当前记录的 field 个数
变量-v var=value
变量名区分大小写,例如:
[root@master ~]# awk -v test="abc" 'BEGIN{print test}'[root@master ~]# awk 'BEGIN{var="name";print var}'name
算术运算
[root@master ~]# awk 'BEGIN{a=5;b=3;print "a + b =",a+b}' + =
+,-,*,/,^,%。例如:
字符串操作
[root@master ~]# awk 'BEGIN { str1="Hello,"; str2="World"; str3 = str1 str2; print str3 }'Hello,World
无符号操作符,表示字符串连接,例如:
赋值操作符:
[root@master ~]# awk 'BEGIN{a=5;b=6;if(a == b) print "a == b";else print "a!=b"}' !=[root@master ~]# awk -F: '{sum+=$3}END{print sum}' /etc/passwd
=,+=,-=,*=,/=,%=,^=,例如:
比较操作符:
>,>=,<,<=,!=,==
模式匹配符:
例如:
[root@master ~]# awk -F: '$1~"root"{print $0}' /etc/passwdroot:x:::root:/root:/bin/bash
~:是否匹配
!~:是否不匹配
逻辑操作符:
[root@master ~]# awk 'BEGIN{a=6;if(a > 0 && a <= 6) print "true";else print "false"}'true
&& 、 || 、 !,例如:
:
function_name(argu1,augu2)
条件表达式(三元运算):
[root@master ~]# awk -F: '{$3>=100?usertype="common user":usertype="sysadmin";printf "%15s:\n",$1,usertype}' /etc/passwd root:sysadmin bin:sysadmin daemon:sysadmin adm:sysadmin lp:sysadmin sync:sysadmin shutdown:sysadmin halt:sysadmin
selection?if-true-expresssion:if-false-expression
empty:空模式,匹配每一行
/regular expression/:仅处理能被此处模式匹配到的行,例如;
[root@master ~]# awk -F: '$NF=="/bin/bash"{printf "%15s,\n",$NF,$1}' /etc/passwd /bin/bash,root
relational expression:关系表达式,结果为“真”有“假”,结果为“真”才会被处理。
Tips:使用模式需要使用双斜线括起来,真:结果为非0值,非空字符串。
[root@master ~]# awk -F: '$3>100{print $1,$3}' /etc/passwdsyd-network polkitd ceph kube etcd gluster nfsnobody chrony redis
awk -F: '$NF=="/bin/bash"{printf "%15s,\n",$NF,$1}' /etc/passwd
awk -F: '$NF~/bash$/{printf "%15s,\n",$NF,$1}' /etc/passwd
-Th|awk '/^\/dev/{print}'
line ranges:行范围,制定startline,endline。
[root@master ~]# awk -F: '/10/,/20/{print $1}' /etc/passwdgames ftp nobody syd-network dbus polkitd postfix sshd ceph kube etcd gluster rpc
BEGIN/END模式
BEGIN{}:仅在开始处理文本之前执行一次
END{}:仅在文本处理完成之一次
[root@master ~]# awk -F: 'BEGIN{print "username uid\n--------------------"}{printf "%-15s:%d\n",$1,$3}END{print "-----------------\nend"}' /etc/passwdusername uid--------------------root : bin : daemon : adm : lp : rpc : rpcuser : nfsnobody : chrony : redis :-----------------end
if(condition) {statements},例如:
[root@master ~]# awk -F: '{if($3>100) print $1,$3}' /etc/passwdsyd-network polkitd ceph kube etcd gluster nfsnobody chrony redis
if(condition) {statments} [else {statments}],例如:
[root@master ~]# awk -F: '{if($3>100) {printf "Common user:%-15s\n",$1} else {printf "sysadmin user:%-15s\n",$1}}' /etc/passwdsysadmin user:root sysadmin user:bin sysadmin user:daemon sysadmin user:adm sysadmin user:lp sysadmin user:sync sysadmin user:shutdown sysadmin user:halt sysadmin user:mail sysadmin user:operator sysadmin user:games
5. 实例
./etc/fstab中每个单词出现的,并按从大到小排序 awk '{for(i=1;i<=NF;i++){words[$i]++}}END{for(key in words)print key,words[key]}' /etc/fstab|sort -k2 -nr awk '{ips[$1]++}END{for(i in ips) print i,ips[i]}' access_.log |column -t|sort -k2 -nr./etc/fstab每个系统类型出现的 awk '!/^#/&&!/^$/{dev[$3]++}END{for(i in dev) print i,dev[i]}' /etc/fstab.ping域名,ping此刻的时间 ping baidu.com|awk '{print $0" "strftime("%Y-%m-%d %H:%M:")}'.利用netstat监控服务是否正常监听 netstat -lntup|awk 'NR>2{if($4 ~/.*:22/) print $0"yes";exit 0}'.web服务器日志状态码 awk '$9~"[0-9]"{stat[$9]++}END{for(i in stat) print i,stat[i]}' access_log
6. 注意事项
awk
同sed
命令类似,只不过sed
擅长取行,awk
命令擅长取列,awk是对文本进行格式化,sed更倾向于对进行;
对于读入的可以根据自己需求对IFS/OFS对输入和进行;
awk非常的强大,但是也是三剑客中最难的,其作为一门单独的语言,我们在Shell编程中学习常用的命令及语法就已经足够我们使用。
7. 小结
本章节我们系统性地学习了awk的语法结构及处理模式,其相较于其他文本处理工具,更适合对文本进行格式化,我们需要在合适的地方使用,其作为Linux系统上非常强大的文本格式工具,也是一门语言,后期需要在实践工作中更多地灵活运用,使得脚本编写更加方便。