- A+
在本博客中,AWK是一个系列文章,本人会尽量以通俗易懂的方式递进的总结awk命令的相关知识点。
awk系列博文直达链接:AWK命令总结之从放弃到入门(通俗易懂,快进来看)
这篇文章中的知识点是建立在前文的基础上的,如果你还没有掌握前文中的知识,请先参考之前的文章。
注:在阅读这篇文章之前,最好已经了解了一些开发的基本语法,比如,for循环、数组的基本使用 等,否则在阅读时 有可能遇到障碍。
前文中提及过,awk其实可以算作一门脚本语言,因为它包含了一个脚本语言的各种语法结构,比如条件判断语句,比如循环语句,那么,awk中能否使用"数组"呢?必须能啊,今天我们就来聊聊awk中的数组。
如果你有过任何一种编程语言的使用经验,那么你一定知道,我们可以通过数组的下标(或者称索引),引用数组中的元素,其他语言中,数组的下标通常由0开始,也就是说,如果想要引用数组中的第1个元素,则需要引用对应的下标"[0]",awk中的数组也是通过引用下标的方法,获取数组中的元素的,但是在awk中,数组元素的下标默认从1开始,但是为了兼容你的使用习惯,我们也可以从0开始设置下标,此处不用纠结,到后面自然会明白,我们先来看一个最简单的示例。
在其他语言中,你可能会习惯性的先"声明"一个数组,在awk中,则不用这样,直接为数组中的元素赋值即可,示例如下。
如上图所示,为了方便示例,上例中使用了BEGIN模式,在BEGIN模式中,存在一个名为"葫芦娃"(拼音)的数组,我们在这个数组中放置了3个元素,第1个元素为"大娃",第2个元素为"二娃",第3个元素为"三娃",如果我们想要引用数组中第二个元素的值,只要引用下标为1的元素即可,正如上图所示,我们使用下标"[1]",获得了huluwa这个数组中第二个元素的值,即"二娃"。
当然,如果你想要看到更多的"葫芦娃",可以在数组里面放置更多的元素。
如上图所示,由于命令太长,可读性可能会降低,为了在编写时提高命令的可读性,我们可以使用Linux命令行的"换行符"进行换行,Linux中,命令行的换行符为反斜杠"\",上述命令换行后,如下。
可以看到,目前葫芦娃数组中已经存在6个葫芦娃了,我们可以获取到我们想要的葫芦娃,换句话说,我们可以通过数组的下标,获取到任何一个元素的值。
我们知道,在动画中,六娃的超能力是"隐身",所以六娃也叫"隐身娃",那么,我们就把上述数组中的第5个元素的值设置为"空字符串"吧,用空字符串表示六娃已经"隐身"了,示例如下。
如上图所示,上例数组中的第5个元素的值被设置为了"空字符串",当我们打印数组中的第5个元素的值时,打印出的值就是"空"(注:"空格"不为"空")。
为什么要举这个例子呢?之所以举这个例子,是因为在awk中,元素的值可以设置为"空",在awk中,将元素的值设置为"空字符串"是合法的。
既然在awk中,元素的值可以为"空",那么我们就不能再根据元素的值是否为"空"去判断元素是否存在了,所以,在awk中,如果你使用如下方法判断数组中的元素是否存在,是不合理的,如下图所示。
正如上图所示,第6个元素明明已经存在,但是通过上述方法判断元素是否存在时,仍然显示对应的元素不存在。
其实,使用上述方法判断元素是否存在之所以不合理,除了上述原因,还有另外一个原因,就是当一个元素不存在于数组时,如果我们直接引用这个不存在的元素,awk会自动创建这个元素,并且默认为这个元素赋值为"空字符串",示例如下。
如上图所示,数组中并没有第7个元素,但是当我们输出第7个元素时,输出了"空",所以,出于此原因,在awk中使用之前的方法判断元素是否为空也是不合理的,因为当我们引用一个不存在于数组中的元素时,这个元素其实已经被赋值为"空字符串"了,如下图所示。
那么,在awk中,应该怎样判断元素是否存在呢?我们可以使用如下语法。
如上图所示,我们可以使用语法 "if(下标 in 数组名)" ,从而判断数组中是否存在对应的元素。
当然,我们还可以使用 "!" 对条件进行取反,如下图所示。
在awk中,数组的下标不仅可以为"数字",还可以为"任意字符串",如果你使用过shell中的数组,你可以把awk的数组比作bash中的"关联数组",示例如下
其实,awk中的数组本来就是"关联数组",之所以先用以数字作为下标的数组举例,是为了让读者能够更好的过度,不过,以数字作为数组下标的数组在某些场景中有一定的优势,但是它本质上也是关联数组,awk默认会把"数字"下标转换为"字符串",所以,本质上它还是一个使用字符串作为下标的关联数组。
使用delete可以删除数组中的元素,如下所示
也可以使用delete删除整个数组,如下所示
到目前为止,我们已经介绍了怎样为数组中的元素赋值、怎样输出数组中的某个元素、以及怎样删除数组中的元素,那么现在,我们来聊聊在awk中怎样输出数组中的所有元素,在awk中,如果想要输出数组中的所有元素,则需要借助for循环语句,还记得在前文中介绍for循环时,有两种for循环语法吗?我们来回顾一下。
1 2 3 4 5 6 7 8 9 |
#for循环语法格式1 for(初始化; 布尔表达式; 更新) { //代码语句 } #for循环语法格式2 for(变量 in 数组) { //代码语句 } |
这两种for循环语法都能够遍历输出数组中的元素,不过第一种for循环语法只能输出以数字作为下标的数组,示例如下
你一定看出来了,我们利用了for循环中的变量"i"与数组中的下标都是"数字"的这一特性,按照顺序输出了数组中的元素值。
那么,当数组中的元素的下标为"无规律的字符串"时,我们该怎么办呢?这时可以使用for循环的第二种语法,示例如下。
注意,在这种语法中,for循环中的变量"i"表示的是元素的下标,而并非表示元素的值,所以,如果想要输出元素的值,则需要使用"print 数组名[变量]"
细心如你,一定发现了一个小问题,当数组中的下标为"字符串"时,元素值输出的顺序与元素在数组中的顺序不同,这是因为awk中的数组本质上是关联数组,所以默认打印出的元素是无序的。
那么你可能会提问了,既然之前说过,数字下标最终也会被转换成 "字符串",本质上也是关联数组,既然都属于关联数组,那么为什么第一种for循环语法能够按照顺序输出数组中的元素值呢?
这就是以数字作为下标的优势,因为第一种for循环语法中的变量"i"为数字,由于for循环的原因,"i"是按照顺序递增的,当"i"的值与下标的值相同时,我们即可按照下标的顺序,输出对应元素的值,换句话说就是,我们是通过下标的顺序,输出对应元素值的顺序,也就是键值定位。但是,即使数组元素的下标为数字,如果使用第二种for循环语法,也不能够按照顺序输出,示例如下。
上例又印证我们之前所说的,awk中的数组本质上就是关联数组。
我想,经过上述对比,你应该已经明白了。
前文中,我们都是手动的为数组中的元素赋值,那么我们能不能将指定的文本分割,然后将分割后的字段自动赋值到数组的元素中呢?答案是必须的,但是如果我们想要实现这样的效果,需要借助于split函数,而我们还没有介绍过函数,所以此处就先跳过了,不过需要提前说明的是,通过split函数生成的数组的下标默认是从1开始的,这就是为什么之前说,awk中数组的下标默认是从1开始的了。
实例应用
在实际的工作中,我们往往会使用数组,统计某些字符出现的次数,比如,我们想要统计日志中每个IP地址出现了多少次,我们就可以利用数组去统计。
但是,统计的时候需要配合一些特殊用法,别着急,我们慢慢聊。
在awk中,我们可以进行数值运算,示例如下
我们将变量a的值设置为1,进行加法计算,每次自加后,再次打印变量a的值,都会加1
这并不难理解,因为上例中,a的值本来就是一个数字。
那么,如果变量a的值是一个字符串,我们能否对变量a进行自加运算呢?我们来试试。
如上图所示,在awk中,当变量a的值为字符串时,竟然也可以进行加法运算,从上例可以看出,awk中,如果字符串参与运算,字符串将被当做数字0进行运算。
那么"空字符串"呢?当空字符串参与运算时,也会被当做数字0吗?我们来试试。
看样子,我们猜的不错,空字符串在参与运算时,也会被当做数字0
之前说过,当我们直接引用一个数组中不存在的元素时,awk会自动创建这个元素,并且为其赋值为"空字符串"。
所以,如果我们引用一个不存在元素,并对其进行自加运算,那么会出现什么效果呢?我们来试一试
如上图所示,当引用了一个不存在的元素时,元素被赋值为空字符串,当对这个元素进行自加运算时,元素的值就变成了1,因为,空字符串在参与运算时,被当做0使用了,所以,综上所述,我们对一个不存在的元素进行自加运算后,这个元素的值就变成了自加运算的次数,自加x次,元素的值就被赋值为x,自加y次,元素的值就被赋值为y,示例如下。
利用这一点,我们就可以统计文本中某些字符出现的次数,比如IP地址,示例如下。
当然,看懂上图中的命令,需要掌握前文中的知识,同时需要理解今天所介绍的知识。
上图中,我们使用了一个空模式,一个END模式。
空模式中,我们随便创建了一个数组,并且将IP地址作为引用元素的下标,进行了引用,所以,当执行到第一行时,我们引用的是count["192.168.1.1"]
很明显,这个元素并不存在,所以,当第一行被空模式中的动作处理完毕后,count["192.168.1.1"]的值已经被赋值为1了。
由于END模式中的动作会最后执行,所以我们先不考虑END模式。
这时,空模式中的动作继续处理下一行,而下一行的IP地址为192.168.1.2
所以,count["192.168.1.2"]第一次参与运算的过程与上述过程同理。
其他IP地址第一次参与运算的过程与上述过程同理。
直到再次遇到相同的IP地址时,使用同样一个IP地址作为下标的元素将会再次被自加,每次遇到相同的IP地址,对应元素的值都会加1。
直到处理完所有行,开始执行END模式中的动作。
而END模式中,我们打印出了count数组中的所有元素的下标,以及元素对应的值。
此刻,count数组中的下标即为IP地址,元素的值即为对应IP地址出现的次数。
最终,我们统计出了每个IP地址出现的次数。
其实,我们就是利用了之前所演示的一个知识点:
我们对一个不存在的元素进行自加运算后,这个元素的值就变成了自加运算的次数
上述过程可能比较绕,如果你之前没有接触过awk,一遍看不懂是很正常的,自己按照上述过程动手做几遍,细细品味一番,相信你会搞明白的。
如果你以后再想统计文本中某类文本出现的"次数",就可以使用上述套路了,活学活用以后,你会发现上述套路特别好使。
比如,如果我们想要统计如下文本中每个人名出现的次数,我们则可以使用如下命令。
关于awk中数组的用法,就先总结到这里,这些知识已经能够满足我的日常使用了,但是这些并不是数组的全部,如果你想要更加深入的了解数组,可以参考官方手册的数组部分,链接如下。
http://www.gnu.org/software/gawk/manual/gawk.html#Arrays
希望这篇文章能够帮助到你,如果你觉的对你有所帮助,欢迎留言,常来呦亲~~

2019年5月14日 上午11:08 沙发
博主,我执行ip筛选的结果和文章的结果不一样,什么原因
2019年9月24日 下午2:42 1层
@33 特地回去看了下 ,一样的啊
2019年4月16日 下午2:58 板凳
感谢博主,写得非常好,学习了
2019年2月1日 下午5:11 地板
赞,一口气读了正则系列 grep,现在又读到了awk,马上结束打卡,感谢
2019年1月16日 下午4:21 4楼
写得深入浅出,很适合小白阅读,基本上都看懂了,感谢。
2018年11月27日 上午10:00 5楼
作者真是大神,看了你的博客受益匪浅
2018年11月21日 下午1:53 6楼
葫芦娃是真的皮
2018年11月21日 下午1:59 1层
@暗颂秋菠 皮皮更健康
2018年10月8日 下午7:07 7楼
第六张图片说使用不存在的元素会创建元素并赋值为空
所以,实例应用的第四张图片代码有问题
问题:所以,如果我们引用一个不存在元素,并对其进行自加运算,那么会出现什么效果呢?我们来试一试
如图片所写,先执行print,此时已经创建了一个为空的元素,所以,代码应带改为
awk ‘BEGIN{test[“ip”]++; print test[“ip”]}’
在统计ip时又运用这个书写漏洞
文档写的很棒,让我等小白轻而易举的就学会了使用awk这神器😄
👍👍👍
2018年10月8日 下午7:13 1层
@t 修改:
去除 在统计ip时又运用这个书写漏洞
😜
2018年9月19日 下午10:10 8楼
有个地方不明白 {count[$1]++}{ for( i in count) } ,for循环中的count指的是count数组名吗?
2018年9月21日 下午5:44 1层
@YIsan 我的理解是:
count是数组名。由于直接引用一个数组中不存在的元素时,AWK会自动创建一个元素。
所以当遇到第一个ip时,由于该元素不存在,所以会创建下标为该ip的元素,且元素值在自加后为1,当再次遇到该元素时,由于相同下标的该元素已经存在且值为1,故会在1的基础上自加,即文章中所说的“对一个不存在的值进行自加运算后,这个元素的值就变成了自加的次数“
2018年9月21日 下午5:52 2层
@Pearl 没毛病~加油~
2018年9月22日 下午12:38 2层
@Pearl 666,兄dei~
2018年9月7日 下午5:29 9楼
迷糊
2018年8月5日 下午3:39 10楼
朱哥威武,很厉害很厉害
2018年2月17日 下午8:23 11楼
hi 博主,这边实际测试了下,输出的内容是按照顺序的,可是博文中是乱序的,这是为何呢?
awk ‘BEGIN{test[“s1″]=”num1”;test[“s2″]=”num2”;test[“s3″]=”num3”;test[“s4″]=”num4”;test[“s5″]=”num5”;for(i in test){print i,test[i]}}’
2017年12月26日 下午4:52 12楼
大神,我这有个小问题,能帮解惑下吗?
需求是:统计日志中静态资源(图片等)走的流量 下面是两种方法,但是我不太明白
1.awk ‘{array_num[$7]++;array_size[$7]+=$10 }END{for(x in array_num){print array_size[x],array_num[x],x}}’ access_2010-12-8.log |sort -rn -k1|head -10 >1.log
我对array_size[$7]+=$10 这个不是太理解?
2.awk ‘{print $7″\t” $10}’ access_2010-12-8.log |awk ‘{S[$1]+=$2;S1[$1]+=1}END{for(i in S) print S[i],S1[i],i}’|sort -rn|head -10 >2.log
这个要达到的目的和上面一样,不太理解S[$1]+=$2;S1[$1]+=1
希望博主能帮下忙,谢谢
2018年5月11日 下午2:06 1层
@随心 array_size[$7]+=$10不就是array_size[$7]=array_size[$7]+$10咯 下面两个同理
2017年11月23日 下午5:28 13楼
滴滴滴滴滴。。。
2017年11月24日 上午10:39 1层
@妞妞 66666666666666················
2017年7月29日 上午8:42 14楼
有个小问题我在BEGIN里对数组赋值打印没有问题,如果我在{}中赋值操作就有BUG了。(不能直接传图片就很烦QWQ)
2017年7月29日 上午9:12 1层
@易知难 这是因为awk在没有指定对应要操作的文本的时候,是从标准输入接收文本进行处理的
说白了,就是如果指定了文件,就一行一行的处理文件中的行,如果没有指定文件,就一行一行的处理你从键盘输入的行
所以,当你不使用BEGIN模式时,直接在{ }中对数组元素赋值时,只有在键盘中输入内容后,数组元素才会真正存在,最终被处理,因为{ }中的动作是每一行都会执行的
不能退出是因为它还在等待你从标准输入中输入内容,其实不止是CTRL+C才能强制退出,使用CTRL+D能从标准输入中退出,表示结束标准输入
这种情况并不是bug,而是完全正常的,可能是因为对概念还是没有完全搞明白,再细想想,加油~~~~~
2017年7月29日 上午10:13 2层
@朱双印 soga,帮大忙了!美滋滋,有大佬带我飞!
2017年7月14日 上午11:47 15楼
当if判断为空字符串,是否可以使用呢?
[root@zero awk]# awk ‘BEGIN{a[0]=”a”;a[1]=”b”;a[2]=”c”;a[3]=””;if(a[3]==””){print a[0]}}’
a
我的结果 centos 6.5环境
2017年7月14日 下午12:54 1层
@zero 没毛病,不过如果想要这种逻辑成立,前提就是在定义数组时需要赋值a[3]==””
如果不赋值,就变成了a[3]==””或者a[3]不存在时都会执行print了,
不过评论发布出来以后看着引号中好像有”空格”似得,其实没有(特此说明)
2017年6月29日 下午9:35 16楼
继续昨天 未完内容
2017年6月29日 下午9:38 1层
@echo 这篇中的实例部分我觉得还是挺有用的,因为在日志中统计次数的需求比较多,所以~加油吧~兄弟
2018年7月13日 上午11:50 2层
@朱双印 确实,这个如果使用得好的话,确定可以省不少的力。虽然看过,但是使用到的时候,再拉出来看一下,还是收获不少。再次感谢朱兄!!!