你好,游客 登录 注册 搜索
背景:
阅读新闻

Linux高级文本处理之gawk关联数组

[日期:2017-02-06] 来源:Linux社区  作者:yolynn [字体: ]

Awk 的数组,都是关联数组,即一个数组包含多个”索引/值”的元素。 索引没必要是一系列 连续的数字,实际上,它可以使字符串或者数字,并且不需要指定数组长度。

语法:

arrayname[string]=value
  • arrayname 是数组名称

  • string 是数组索引

  • value 是为数组元素赋的值

访问 awk 数组的元素

如果要访问数组中的某个特定元素,使用 arrayname[index] 即可返回该索引中的值。

实例1:

[root@localhost ~]# awk '
>BEGIN{ item[101]="HD Camcorder";
>item["102"]="Refrigerator";
>item[103]="MP3 Player";
>item["na"]="Young"
>print item[101];  
>print item["102"];    #注意带引号不带引号awk都视为字符串来处理
>print item[103];
>print item["na"];}'   #字符串索引需要加双引号
HD Camcorder
Refrigerator
MP3 Player
Young

注意:

  • 数组索引没有顺序,甚至没有从 0 或 1 开始.

  • 数组索引可以是字符串,数组的最后一个元素就是字符串索引,即”na”

  • Awk 中在使用数组前,不需要初始化甚至定义数组,也不需要指定数组的长度。

  • Awk 数组的命名规范和 awk 变量命名规范相同。

以 awk 的角度来说,数组的索引通常是字符串,即是你使用数组作为索引, awk 也会当 做字符串来处理。下面的写法是等价的:

Item[101]="HD Camcorder"
Item["101"]="HD Camcorder"

一、引用数组元素

如果试图访问一个不存在的数组元素, awk 会自动以访问时指定的索引建立该元素,并赋予 null 值。 为了避免这种情况,在使用前最后检测元素是否存在。

使用 if 语句可以检测元素是否存在,如果返回 true,说明改元素存在于数组中。

if ( index in array-name )

实例2:一个简单的引用数组元素的例子

[root@localhost ~]# cat arr.awk 
BEGIN {
    x = item[55];  #在引用前没有赋任何值,所以在引用是 awk 自动创建该元素并赋 null 值
    if ( 55 in item )
        print "Array index 55 contains",item[55];
    item[101]="HD Camcorder";
    if ( 101 in item )
        print "Array index 101 contains",item[101];
    if ( 1010 in item )  #不存在,因此检查索引值时,返回 false,不会被打印
        print "Array index 1010 contains",item[1010];
}
[root@localhost ~]# awk -f arr.awk 
Array index 55 contains 
Array index 101 contains HD Camcorder

二、使用循环遍历 awk 数组

如果要访问数组中的所有元素, 可以使用 for 的一个特殊用法来遍历数组的所有索引:

语法:

for ( var in arrayname )
actions

说明:

  • var 是变量名称

  • in 是关键字

  • arrayname 是数组名

  • actions 是一系列要执行的 awk 语句,如果有多条语句,必须包含在{ }中。 通过把索引值赋给变量 var,循环体可以把所有语句应用到数组中所有的元素上。

实例1:将数组中元素全部打印出来

[root@localhost ~]# cat arr-for.awk 
BEGIN {
    item[101]="HD Camcorder";
    item[102]="Refrigerator";
    item[103]="MP3 Player";
    item[104]="Tennis Racket";
    item[105]="Laser Printer";
    item[1001]="Tennis Ball";
    item[55]="Laptop";
    item["no"]="Not Available";

    for(x in item)  #x 是变量名,用来存放数组索引,无需制定条件,awk自行判断
        print item[x];
}
[root@localhost ~]# awk -f arr-for.awk 
Not Available
Laptop
HD Camcorder
Refrigerator
MP3 Player
Tennis Racket
Laser Printer
Tennis Ball

三、删除数组元素

如果要删除特定的数组元素,使用 delete 语句。一旦删除了某个元素,就再也获取不到它 的值了。

语法:

delete arrayname[index];

删除数组内所有元素:

for (var in array)
delete array[var]

在 GAWK 中,可以使用单个 delete 命令来删除数组的所有元素:

Delete array

实例1:

[root@localhost ~]# awk '
>BEGIN{item[101]="HD Camcorder";
>item[102]="Refrigerator";
>item[103]="MP3 Player";
>delete item[101];
>print item[101];print item[102];
>for(x in item) delete item[x]; #使用for循环删除全部数组
>print item[102];print item[103];}'

Refrigerator


[root@localhost ~]#

实例2:

[root@localhost ~]# awk '
>BEGIN{item[1]="a"; 
>item[2]="b";item[3]="c";
>delete item;   #使用delete直接加数组名称删除全部数组
>for(x in item) print item[x];}'

四、多维数组

虽然 awk 只支持一维数组,但可以使用一维数组来模拟多维数组。

实例1:

[root@localhost ~]# cat array-multi.awk
BEGIN {
item["1,1"]=10;
item["1,2"]=20;
item["2,1"]=30;
item["2,2"]=40
for (x in item)
print item[x]
}
[root@localhost ~]# awk -f array-multi.awk
30
20
40
10

说明:即使使用了”1,1”作为索引值,它也不是两个索引,仍然是单个字符串索引,值为”1,1”。所 以item[“1,1”]=10,实际上是把 10 赋给一维数组中索引”1,1”代表的值。

实例2:将双引号去掉

[root@localhost ~]# cat array-multi2.awk
BEGIN {
item[1,1]=10;
item[1,2]=20;
item[2,1]=30;
item[2,2]=40
for (x in item)
print item[x]
}
[root@localhost ~]# awk -f array-multi2.awk
10
30
20
40

说明:上面的例子仍然可以运行,但是结果有所不同。在多维数组中,如果没有把下标用引号引住, awk 会使用”\034”作为下标分隔符。

当指定元素 item[1,2]时,它会被转换为 item[“1\0342”]。 Awk 用把两个下标用”\034”连接起 来并转换为字符串。

实例3:

[root@localhost ~]# cat 034.awk 
BEGIN {
    item["1,1"]=10;
    item["1,2"]=20;
    item[2,1]=30;
    item[2,2]=40;
    for(x in item)
        print "Index",x,"contains",item[x];
}
[root@localhost ~]# awk -f 034.awk 
Index 1,2 contains 20
Index 21 contains 30
Index 22 contains 40
Index 1,1 contains 10

说明:

索引”1,1”和”1,2”放在了引号中,所以被当做一维数组索引, awk 没有使用下标分 隔符,因此,索引值被原封不动地输出。

所以 2,1 和 2,2 没有放在引号中,所以被当做多维数组索引, awk 使用下标分隔符 来处理,因此索引变成”2\0341”和”2\0342”,于是在两个下标直接输出了非打印字符 “\034”

五、SUBSEP 下标分隔符

通过变量 SUBSEP 可以把默认的下标分隔符改成任意字符。

实例1:

[root@localhost ~]# cat subsep.awk 
BEGIN {
    SUBSEP=":";
    item["1,1"]=10;
    item["1,2"]=20;
    item[2,1]=30;
    item[2,2]=40;
    for(x in item)
        print "Index",x,"contains",item[x];
}
[root@localhost ~]# awk -f subsep.awk 
Index 1,2 contains 20
Index 2:1 contains 30
Index 2:2 contains 40
Index 1,1 contains 10

说明:索引”1,1”和”1,2”由于放在了引号中而没有使用 SUBSEP 变量。

注意:使用多维数组时,最好不要给索引值加引号,直接使用SUBSEP变量制定索引分隔符。

六、用 asort 为数组排序

asort 函数重新为元素值排序,并且把索引重置为从 1 到 n 的值,此处 n 代表数组元素个数。

实例1:

[root@localhost ~]# cat asort.awk 
BEGIN {
    item[101]="HD Camcorder";
    item[102]="Refrigerator";item[103]="MP3 Player";
    item[104]="Tennis Racket";
    item[105]="Laser Printer";
    item[1001]="Tennis Ball";
    item[55]="Laptop";
    item["na"]="Not Available";
    print "---------- Before asort -------------"
    for(x in item)
        print "Index",x,"contains",item[x]
    total = asort(item);
    print "---------- After asort -------------"
    for(x in item)
        print "Index",x,"contains",item[x]
    print "Return value from asort:",total;
}
[root@localhost ~]# awk -f asort.awk 
---------- Before asort -------------
Index 55 contains Laptop
Index 101 contains HD Camcorder
Index 102 contains Refrigerator
Index 103 contains MP3 Player
Index 104 contains Tennis Racket
Index 105 contains Laser Printer
Index na contains Not Available
Index 1001 contains Tennis Ball
---------- After asort -------------  #awk数组索引是从1开始的不是0
Index 4 contains MP3 Player
Index 5 contains Not Available
Index 6 contains Refrigerator
Index 7 contains Tennis Ball
Index 8 contains Tennis Racket
Index 1 contains HD Camcorder
Index 2 contains Laptop
Index 3 contains Laser Printer
Return value from asort: 8

注意:一旦调用 asort 函数,数组原始的索引值就不复存在了,索引并不是按照1-8排序而是随机排序。

实例2:增加索引排序功能

[root@localhost ~] cat asort1.awk
BEGIN {
item[101]="HD Camcorder";
item[102]="Refrigerator";item[103]="MP3 Player";
item[104]="Tennis Racket";
item[105]="Laser Printer";
item[1001]="Tennis Ball";
item[55]="Laptop";
item["na"]="Not Available";
total = asort(item);
for(i=1;i<=total;i++)  #添加for循环控制索引输出的顺序
print "Index",i,"contains",item[i]
}
[root@localhost ~] awk -f asort1.awk
Index 1 contains HD Camcorder
Index 2 contains Laptop
Index 3 contains Laser Printer
Index 4 contains MP3 Player
Index 5 contains Not Available
Index 6 contains Refrigerator
Index 7 contains Tennis Ball
Index 8 contains Tennis Racket

七、用 asorti 为索引排序

和以元素值排序相似,也可以取出所有索引值,排序,然后把他们保存在新数组中。

说明:

  • asorti 函数为索引值(不是元素值)排序,并且把排序后的元素值当做元素值保存。

  • 如果使用 asorti(state)将会丢失原始元素值,即索引值变成了元素值。因此为了保险起见,通常给 asorti 传递两个参数,即 asorti(state,statebbr).这样一来,原始数组state 就不会被覆盖了。

实例1:

[root@localhost ~]# cat asorti.awk
BEGIN {
state["TX"]="Texas";
state["PA"]="Pennsylvania";
state["NV"]="Nevada";
state["CA"]="California";
state["AL"]="Alabama";
print "-------------- Function: asort -----------------"
total = asort(state,statedesc);
for(i=1;i<=total;i++)
print "Index",i,"contains",statedesc[i];
print "-------------- Function: asorti -----------------"
total = asorti(state,stateabbr);
for(i=1;i<=total;i++)   #索引按顺序输出也需要自行排序
print "Index",i,"contains",stateabbr[i];
}
[root@localhost ~]# awk -f asorti.awk
-------------- Function: asort -----------------
Index 1 contains Alabama
Index 2 contains California
Index 3 contains Nevada
Index 4 contains Pennsylvania
Index 5 contains Texas
-------------- Function: asorti -----------------
Index 1 contains AL
Index 2 contains CA
Index 3 contains NV
Index 4 contains PA
Index 5 contains TX

补充实例:利用数组删除重复行

[root@localhost ~]# cat alpha 

a
a
a
b
c
b
d
d
e
e
f
f
f
f
g
[root@localhost ~]# awk '!a[$0]++' alpha 
a
b
c
d
e
f
g

注解:

为何上面的命令将重复的行去掉了呢?原因如下:首先,当读入第一个字符a时,关联数组array的以a为索引的值为空,即array[a]=0,将此取反为1,逻辑上为真,则输出第一行,然后自相加为2。其次,当读入第二个值b时,同理可知为1array也为1。当第二次读入a时,因为array[a]的值已经为2,(逻辑)取反之后为0,逻辑上是假,则不会输出,自相加最后为1

注意:第一点,!的运算顺序比++要更优先;第二点,++是在print之后才会执行。

本文永久更新链接地址http://www.linuxidc.com/Linux/2017-02/140276.htm

linux
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款