# awk从放弃到入门（9）：awk数组详解

- 34

- A+

所属分类：Linux基础

> 在本博客中，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循环语法吗？我们来回顾一下。

Shell
#for循环语法格式1 for(初始化; 布尔表达式; 更新) { //代码语句 } #for循环语法格式2 for(变量 in 数组) { //代码语句 }

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

希望这篇文章能够帮助到你，如果你觉的对你有所帮助，欢迎留言，常来呦亲~~

我的微信公众号
关注"实用运维笔记"微信公众号，当博客中有新文章时，可第一时间得知哦~
赞 102
赏
分享

- awk

- 版权声明：本站原创文章，于2017年6月25日22:35:57，由 朱双印 发表，共 4537 字。
- 如需转载，请在文章头部注明出处：awk从放弃到入门（9）：awk数组详解 | 朱双印博客

awk从放弃到入门（11）：拾遗之”三元运算”与”打印奇偶行”
awk从放弃到入门（10）：awk内置函数
awk从放弃到入门（8）：awk动作总结之二
awk从放弃到入门（7）：awk动作总结之一

### 发表评论取消回复

欢迎 pangzi 再次光临！
修改信息

正在提交, 请稍候...
#

昵称*

邮箱*

网址

有回复时邮件通知我

## 目前评论：34 其中：访客 28 博主 6

2. test 0
   回复 2020年10月18日 下午3:52 沙发

   [root@linux ~]# awk ‘BEGIN{printf “%-10s\t %s\n”,”ip地址”,”出现次数”}{count[$1]++}END{for(i in count){printf “%-10s\t %-s\n”, i,count[i]}}’ test5.txt
   ip地址 出现次数
   1
   192.168.1.1 960
   192.168.1.2 576
   192.168.1.3 384
   192.168.1.4 384
   192.168.1.5 192

   老师帮忙看下为啥第二行打印出来有个1 去掉BEGIN的内容没有问题

4. adu 1
   回复 2020年10月16日 下午3:41 板凳

   经常光顾博主的教程文章，如果想要从多行文本中统计每一行内容中某个字符出现的次数，并把出现次数和该行内容输出 ，数组如何实现？

6. asi 0
   回复 2020年5月20日 上午12:05 地板

   讲得通俗易懂, 早点看到这篇文章就好了

8. 迷之夏天 3
   回复 2020年4月24日 下午1:23 4楼

   胡撸娃。。。好吧，博主么么么哒先～count[$i]++这个套路真的精巧，感谢了～

10. hope？ 0
    回复 2019年12月16日 下午2:32 5楼

    如何才能像作者您这么优秀。。。。

12. 33 0
    回复 2019年5月14日 上午11:08 6楼

    博主，我执行ip筛选的结果和文章的结果不一样，什么原因

    - chen 1
      回复 2019年9月24日 下午2:42 1层

      @33 特地回去看了下 ，一样的啊

14. copecode 0
    回复 2019年4月16日 下午2:58 7楼

    感谢博主，写得非常好，学习了

16. lnking 1
    回复 2019年2月1日 下午5:11 8楼

    赞，一口气读了正则系列 grep，现在又读到了awk，马上结束打卡，感谢

18. 123456 1
    回复 2019年1月16日 下午4:21 9楼

    写得深入浅出，很适合小白阅读，基本上都看懂了，感谢。

20. Scar 1
    回复 2018年11月27日 上午10:00 10楼

    作者真是大神，看了你的博客受益匪浅

22. 暗颂秋菠 0
    回复 2018年11月21日 下午1:53 11楼

    葫芦娃是真的皮

    - 朱双印 Admin
      回复 2018年11月21日 下午1:59 1层

      @暗颂秋菠 皮皮更健康

24. t 2
    回复 2018年10月8日 下午7:07 12楼

    第六张图片说使用不存在的元素会创建元素并赋值为空
    所以，实例应用的第四张图片代码有问题
    问题：所以，如果我们引用一个不存在元素，并对其进行自加运算，那么会出现什么效果呢？我们来试一试
    如图片所写，先执行print，此时已经创建了一个为空的元素，所以，代码应带改为
    awk ‘BEGIN{test[“ip”]++; print test[“ip”]}’
    在统计ip时又运用这个书写漏洞

    文档写的很棒，让我等小白轻而易举的就学会了使用awk这神器😄
    👍👍👍

    - t 2
      回复 2018年10月8日 下午7:13 1层

      @t 修改：
      去除 在统计ip时又运用这个书写漏洞
      😜

26. YIsan 1
    回复 2018年9月19日 下午10:10 13楼

    有个地方不明白 ｛count[$1]++｝{ for( i in count) } ，for循环中的count指的是count数组名吗？

    - Pearl 2
      回复 2018年9月21日 下午5:44 1层

      @YIsan 我的理解是：
      count是数组名。由于直接引用一个数组中不存在的元素时，AWK会自动创建一个元素。
      所以当遇到第一个ip时，由于该元素不存在，所以会创建下标为该ip的元素，且元素值在自加后为1，当再次遇到该元素时，由于相同下标的该元素已经存在且值为1，故会在1的基础上自加，即文章中所说的“对一个不存在的值进行自加运算后，这个元素的值就变成了自加的次数“

      - 朱双印 Admin
        回复 2018年9月21日 下午5:52 2层

        @Pearl 没毛病~加油~

      - YIsan 1
        回复 2018年9月22日 下午12:38 2层

        @Pearl 666，兄dei~

28. 小土豆 4
    回复 2018年9月7日 下午5:29 14楼

    迷糊

30. LornBlood 0
    回复 2018年8月5日 下午3:39 15楼

    朱哥威武，很厉害很厉害

32. protester 1
    回复 2018年2月17日 下午8:23 16楼

    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]}}’

34. 随心 2
    回复 2017年12月26日 下午4:52 17楼

    大神，我这有个小问题，能帮解惑下吗？
    需求是：统计日志中静态资源（图片等）走的流量 下面是两种方法，但是我不太明白
    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
    希望博主能帮下忙，谢谢

    - aa 0
      回复 2018年5月11日 下午2:06 1层

      @随心 array_size[$7]+=$10不就是array_size[$7]=array_size[$7]+$10咯 下面两个同理

36. 妞妞 4
    回复 2017年11月23日 下午5:28 18楼

    滴滴滴滴滴。。。

    - 朱双印 Admin
      回复 2017年11月24日 上午10:39 1层

      @妞妞 66666666666666················

38. 易知难 4
    回复 2017年7月29日 上午8:42 19楼

    有个小问题我在BEGIN里对数组赋值打印没有问题，如果我在｛｝中赋值操作就有BUG了。(不能直接传图片就很烦QWQ)

    - 朱双印 Admin
      回复 2017年7月29日 上午9:12 1层

      @易知难 这是因为awk在没有指定对应要操作的文本的时候，是从标准输入接收文本进行处理的
      说白了，就是如果指定了文件，就一行一行的处理文件中的行，如果没有指定文件，就一行一行的处理你从键盘输入的行
      所以，当你不使用BEGIN模式时，直接在{ }中对数组元素赋值时，只有在键盘中输入内容后，数组元素才会真正存在，最终被处理，因为{ }中的动作是每一行都会执行的
      不能退出是因为它还在等待你从标准输入中输入内容，其实不止是CTRL+C才能强制退出，使用CTRL+D能从标准输入中退出，表示结束标准输入
      这种情况并不是bug，而是完全正常的，可能是因为对概念还是没有完全搞明白，再细想想，加油~~~~~

      - 易知难 4
        回复 2017年7月29日 上午10:13 2层

        @朱双印 soga，帮大忙了！美滋滋，有大佬带我飞！

40. zero 1
    回复 2017年7月14日 上午11:47 20楼

    当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环境

    - 朱双印 Admin
      回复 2017年7月14日 下午12:54 1层

      @zero 没毛病，不过如果想要这种逻辑成立，前提就是在定义数组时需要赋值a[3]==””
      如果不赋值，就变成了a[3]==””或者a[3]不存在时都会执行print了，
      不过评论发布出来以后看着引号中好像有”空格”似得，其实没有（特此说明）

42. echo 6
    回复 2017年6月29日 下午9:35 21楼

    继续昨天 未完内容

    - 朱双印 Admin
      回复 2017年6月29日 下午9:38 1层

      @echo 这篇中的实例部分我觉得还是挺有用的，因为在日志中统计次数的需求比较多，所以~加油吧~兄弟

      - drgcaosheng 4
        回复 2018年7月13日 上午11:50 2层

        @朱双印 确实，这个如果使用得好的话，确定可以省不少的力。虽然看过，但是使用到的时候，再拉出来看一下，还是收获不少。再次感谢朱兄！！！

- 文章目录

×
×
拖拽到此处
图片将完成下载