AWK is what?
AWK是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
AWK基本用法?
awk [options] 'BEGIN{ action;… } pattern{ action;… } END{ action;… }' file
awk 程序通常由:BEGIN语句块、能够使用模式匹配的通用语句块、END语句块,共3部分组成
分割符、域和记录 awk执行时,由分隔符分隔的字段(域)标记1,2..n称为域标识,0为所有域,注意:和shell中变量符含义不同,文件的每一行称为记录,省略action,则默认执行 print 0 的操作。
AWK工作原理?
第一步:执行BEGIN{action;… }语句块中的语句 第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{ action;… }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。 第三步:当读至输入流末尾时,执行END{action;…}语句块BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中 END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块,pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块。
通俗理解:awk工作流程是这样的:先执行BEGIN,然后读取文件,读入有/n换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,0则表示所有域,1表示第一个域,
1:AWK常用变量
FS:输入字段分隔符,默认为空白字符
awk -v FS=':' '{print $1,FS,$3}' /etc/passwd
awk –F: '{print $1,$3,$7}' /etc/passwd
OFS:输出字段分隔符,默认为空白字符
[root@localhost ~]# awk -F: '{print $1,$3,$7}' /etc/passwd
root 0 /bin/bash
bin 1 /sbin/nologin
daemon 2 /sbin/nologin
adm 3 /sbin/nologin
lp 4 /sbin/nologin
[root@localhost ~]# awk -F: -v OFS=':' '{print $1,$3,$7}' /etc/passwd
pulse:497:/sbin/nologin
sshd:74:/sbin/nologin
tcpdump:72:/sbin/nologin
junchao:500:/bin/bash
qijunchao:501:/bin/bash
NF:字段数量
awk -F: ‘{print NF}’ /etc/fstab,引用内置变量不用$ #输出/etc/fstab下每一行域的个数
awk -F: '{print $(NF-1)}' /etc/passwd #表示输出倒数第二域的内容
NR:行号
[root@localhost ~]# awk '{print NR}' /etc/fstab
1
2
3
4
[root@localhost ~]# awk END'{print NR}' /etc/fstab
4
[root@localhost ~]#
printf格式化输出
[root@localhost ~]# awk -F: '{printf "Username:%s UID:%s\n",$1,$3}' /etc/passwd
Username:root UID:0
Username:bin UID:1
#左对齐
[root@localhost ~]# awk -F: '{printf "Username:%-15s UID:%s\n",$1,$3}' /etc/passwd
Username:root UID:0
Username:bin UID:1
三目表达式 selector?if-true-expression:if-false-expression
[root@localhost ~]# awk -F: '{$3>=500?usertype="Common User":usertype="Sys User";printf "UserName:%-15s Type:%s\n",$1,usertype}' /etc/passwd
UserName:root Type:Sys User
UserName:bin Type:Sys User
UserName:junchao Type:Common User
UserName:qijunchao Type:Common User
模式匹配符:~:左边是否和右边匹配包含!~:是否不匹配
[root@localhost ~]# awk '$0 ~ "^root" {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@localhost ~]# awk -v FS=: '$3 == 0 {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@localhost ~]#
lag'}' 结果为abcd awk '{print "
2:AWK控制语句
条件语句:
if... else if...else
[root@localhost ~]# awk -v FS=: '{if($3>=1000){usertype="Common User"}else if($3==0)
{usertype="SuperUser"}else{usertype="SysUser"}printf"UserName:%-15sType:%s\n",$1,usertype}' /etc/passwd
UserName:root Type:Super User
UserName:bin Type:Sys User
循环语句:
while
[root@localhost ~]# cat /boot/grub/grub.conf |awk '/^[[:space:]]*kernel/{i=1;while(i<=NF){print $i,length($i);i++}}'
kernel 6
/vmlinuz-2.6.32-696.el6.x86_64 30
ro 2
root=UUID=d98fccb0-b74e-4de3-9953-27b74542267a 46
rd_NO_LUKS 10
rd_NO_LVM 9
LANG=en_US.UTF-8 16
do...while
[root@localhost ~]# awk 'BEGIN{ total=0;i=0;do{ total+=i;i++;}while(i<=100);print total}'
5050
for循环
[root@localhost certs]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
linux16 7
/vmlinuz-3.10.0-862.el7.x86_64 30
root=UUID=fbbc6de7-b727-41f8-b18b-90e56dc412b3 46
ro 2
crashkernel=auto 16
rhgb 4
quiet 5
break和continue
[root@localhost certs]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==10)break;sum+=i}print sum}'
45
[root@localhost certs]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==10)continue;sum+=i}print sum}'
5040
[root@localhost certs]#
next: 提前结束对本行处理而直接进入下一行处理(awk自身循环)
[root@localhost certs]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
root 0
daemon 2
lp 4
shutdown 6
mail 8
games 12
3:数组
awk中的数组的下标可以是数字和字母,称为关联数组。
用变量作为数组下标。如:
$ awk {name[x++]=$2};END{for(i=0;i<NR;i++) print i,name[i]}' test
数组name中的下标是一个自定义变量x,awk初始化x的值为0,在每次使用后增加1。第二个域的值被赋给name数组的各个元素。在END模块中,for循环被用于循环整个数组,从下标为0的元素开始,打印那些存储在数组中的值。因为下标是关健字,所以它不一定从0开始,可以从任何值开始。
用域值作为数组的下标。for (index_value in array) statement。
例:统计/etc/passwd下bash类型以及个数
[root@localhost ~]# awk -F: '{line[$7]++}END{for(i in line){print i,line[i]}}' /etc/passwd
/sbin/shutdown 1
/bin/bash 3
/sbin/nologin 26
/sbin/halt 1
/bin/sync 1
4:awk的内建函数
字符串函数
sub sub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容,并将第一个匹配的内容替换为s
[root@localhost ~]# echo "2018:08:29 10:08:30" |awk 'sub(/:/,"-",$1)'
2018-08:29 10:08:30
gsub gsub(r,s,[t]):对t字符串进行搜索r表示的模式匹配的内容
[root@localhost ~]# echo "2018:08:29 10:08:30" |awk 'gsub(/:/,"-",$1)'
2018-08-29 10:08:30
对t字符串进行搜索r表示的模式匹配的内容,并全部替换为s所表示的内容
[root@localhost ~]# echo "2018:08:29 10:08:30" |awk 'gsub(/:/,"-",$0)'
2018-08-29 10-08-30
查找字符串(index使用)
[root@localhost ~]# awk 'BEGIN{info="this is a test2018test!";print index(info,"test")?"ok":"no found";}'
ok
[root@localhost ~]# awk 'BEGIN{info="this is a test2018test!";print index(info,"kobe")?"ok":"no found";}'
no found
正则表达式匹配查找(match使用)
[root@localhost ~]# awk 'BEGIN{info="this is a test2018test!";print match(info,/[0-9]+/)?"ok":"no found";}'
ok
[root@localhost ~]# awk 'BEGIN{info="this is a testhellotest!";print match(info,/[0-9]+/)?"ok":"no found";}'
no found
截取字符串(substr使用)
[root@localhost ~]# awk 'BEGIN{info="this is a test2018test!";print substr(info,4,10);}'
s is a tes
字符串分割(split使用)
[root@localhost ~]# awk 'BEGIN{info="this is a test";split(info,tA," ");print length(tA);for(k in tA){print k,tA[k];}}'
4
4 test
1 this
2 is
3 a
分割info,动态创建数组tA,这里比较有意思,awk for …in 循环,是一个无序的循环。 并不是从数组下标1…n ,因此使用时候需要注意。
如何把一行竖排的数据转换成横排?
[root@localhost ~]# awk '{printf("%s,",$1)}' testfile
hello,nihai,haizi,yuanren,daren,test,file,kobe,manba,