Bash的关联数组详解Bash支持关联数组(associativearrays),可以使用任意字符串或整数作为下标访问数组元素。关联数组的下标和值称为键值对,它们是一一对应的。键是唯一的,值未必是唯一的。在使用关联数组之前,您需要使用declare-Aarray_name将array_name变量显式声明为关联数组。查看helpdeclare对-A选项的解释如下:-AtomakeNAMEsassociativearrays(ifsupported)例如,下面的语句定义了一个名为filetypes的关联数组,并为该数组赋值:$declare-Afiletypes=([txt]=text[sh]=shell[mk]=makefile)$filetypes[c]="csourcefile"使用数组名赋值时,需要用括号()将所有值括起来。在关联数组中,方括号[]中的值是键。赋给方括号[]的值就是key对应的值。不同的键值对之间用空格隔开。请注意,它们不是用逗号分隔的。也可以使用filetypes[key]=value对指定的关联数组元素单独赋值。如果给定的键之前不存在,bash将自动创建它。如果已经存在,则将其值修改为value对应的值。根据前面定义的filetypes的数组名:${!filetypes[*]}:获取关联数组的所有键名,注意有个感叹号'!'在文件类型前面。$echo${!filetypes[*]}txtshcmk${!filetypes[@]}:获取关联数组的所有键。使用*和@的区别将在后面解释。$echo${!filetypes[@]}txtshcmk${filetypes[*]}:获取关联数组的所有值。与获取键名的表达式相比,前面的感叹号'!'不见了。$echo${filetypes[*]}textshellcsourcefilemakefile${filetypes[@]}:获取一个关联数组的所有值。$echo${filetypes[@]}textshellcsourcefilemakefile${#filetypes[*]}:获取关联数组的长度,即元素个数。请注意文件类型前面的井号“#”。$echo${#filetypes[*]}4${#filetypes[@]}:获取关联数组的长度,即元素个数$echo${#filetypes[@]}4${filetypes[key]}:获取key键名对应的值。请注意,花括号{}是必需的。$echo${filetypes[sh]}shell$echo$filetypes[sh][sh]#可以看到,不加括号的情况下是获取不到数组元素的值的。参见manbash的Arrays部分,其中解释了这些表达式的含义,同时也提到了使用*和@的区别。具体区别贴如下:如果单词是双引号,${name[*]}扩展为单个单词,每个数组成员的值由IFS特殊变量的第一个字符分隔,而${name[@]}将name的每个元素扩展为一个单独的词。当没有数组成员时,${name[@]}展开为空。${!name[@]}和${!name[*]}展开为数组变量名称中分配的索引。双引号中的处理类似于特殊参数@和*在双引号中的扩展。即,在使用*用双引号将整个表达式括起来时,例如“${!name[*]}”或“${name[*]}”,所有的值都会组合成一个字符串。使用@时,如果将整个表达式用双引号括起来,例如“${!name[@]}”或“${name[@]}”,您将得到一个字符串数组。每个数组元素都将包含在双引号中,因此数组元素中的空格本身不会导致拆分成几个单词。具体如下例所示,这也是一个遍历数组元素的例子:$forkeyin"${filetypes[*]}";做echo"****:"$key;完成****:文本shellc源文件makefile$forkeyin"${filetypes[@]}";做echo"@@@@:"$key;done@@@@:text@@@@:shell@@@@:csourcefile@@@@:makefile可以看到"${filetypes[*]}"只产生一个字符串,for循环只遍历一次。而“${filetypes[@]}”产生多个字符串,for循环遍历多次,是一个字符串数组。并且给定的“csourcefile”字符串没有被空格分隔成几个单词。上面的示例还演示了如何使用for命令遍历数组元素。可以使用declare-p命令查看数组具体的键值对关系:$declare-pfiletypesdeclare-Afiletypes='([txt]="text"[sh]="shell"[c]="csourcefile"[mk]="makefile")'Bash一维数组详解Bash只支持一维索引数组(one-dimensionalindexedarray),不支持二维数组。声明一维数组的方法是:declare-aarray_name。由于bash不需要显式指定变量的类型,所以不需要声明,可以直接以数组的形式赋值给变量。参见helpdeclare对-a选项的解释如下:-a使NAMEs索引数组(如果支持)使用declare-a声明的数组默认使用数字作为数组下标,不需要指定数组长度.赋值方法说明如下:array=(value1value2value3...valueN):该方法从数组下标0开始给数组元素赋值,不同的值用空格隔开。给定的值可以是数字和字符String等。$declare-aarray=(12"30""40"5)$echo${array[@]}1230405array=([0]=var1[1]=var2[2]=var3...[n]=varN):该方法显式提供数组下标,指定给元素赋值,给定的数组下标可以不连续。$declare-aarray=([0]=1[1]=2[3]="30"[6]="60"[9]=9)$echo${array[@]}#使用${array[@]}获取所有数组元素的值1230609$echo${array[5]}#上面赋值时,跳过了数组下标5,所以它对应的值为空$declare-parray#使用declare-p命令查看,会打印出所有赋值的元素declare-aarray='([0]="1"[1]="2"[3]="30"[6]="60"[9]="9")'array[0]=value1;数组[1]=值2;...;array[n]=varN:该方法为数组元素单独赋值。$未设置数组;声明-aarray$array[0]=0;数组[1]=1;array[7]="70"$declare-parraydeclare-aarray='([0]="0"[1]="1"[7]="70")'一维数组的其他用途是和上一篇介绍的关联数组的用法一样。例如可以使用${array[@]}获取所有数组元素的值,使用${#array[@]}获取数组元素的个数等,可以参考如下代码片段进行遍历一维数组元素:foritemin"${array[@]}";doecho$itemdone一维数组使用正整数索引数组元素。如果提供一个负整数下标值,它有特殊的意义,意味着从数组的末尾向前索引。例如,array[-1]将索引到数组的最后一个元素,array[-2]将索引到数组的倒数第二个元素,依此类推。具体例子如下:$declare-aarray=([0]=0[1]=1[2]="20"[3]=3)$echo${array[-1]},${array[-3]}3,1注意:虽然declare-a声明的数组使用数字作为数组下标,但是使用字符串作为数组下标不会报错。实际测试有一些奇怪之处。具体例子如下:$declare-aarray=([0]=0[1]=1[2]="20"[3]=3)$array[index]=1000$echo${array[index]}1000$array[new]=2000$echo${array[index]}2000$echo${array[new]}2000$declare-parraydeclare-aarray='([0]="2000"[1]="1"[2]="20"[3]="3")'可以看到,给array[index]元素赋值没有报错,可以正常获取到它的值通过使用${array[index]}。但是给array[new]赋值2000后,用${array[index]}打印字符串index下标对应的数组元素的值,发现变成了2000,这和${array[new]}打印的值。看起来这两个字符串下标与同一个数组元素相关。其实它们都对应数组元素0。可以看到,上面的declare-parray命令打印出来[0]的元素值变成了2000。查看manbash的Arrays部分,说明如下:Indexedarraysarereferencedusingintegers(includingarithmeticexpressions)andarebasedzero;如果使用语法name[subscript]=value分配给任何变量,则会自动创建一个索引数组。下标被视为必须计算为数字的算术表达式。引用不带下标的数组变量等同于引用下标为0的数组。也就是说,索引数组的下标必须是数字,或者是一个算术表达式(arithmeticexpressions)计算出的数字。如果未提供数组索引,则默认使用数组索引0。由于bash的算术表达式在获取变量值时不需要使用$符号,所以上面的array[index]实际上等价于array[$index],即获取index变量的值作为数组下标。如果给定的index变量没有值,相当于没有提供数组下标,默认使用数组下标0,所以给array[index]赋值实际上就是给array[0]赋值。同理,给array[new]赋值也是给array[0]赋值,你会看到array[index]的值也变了。如果index变量的值不为0,new变量没有值,那么给array[index]赋值不会影响array[new]。在上面例子的基础上,继续执行如下语句:$index=1$array[index]=100$echo"array[index]=${array[index]},array[1]=${array[1]}"array[index]=100,array[1]=100$array[new]=900$echo"array[new]=${array[new]},array[0]=${array[0]},array[index]=${array[index]}"array[new]=900,array[0]=900,array[index]=100$recurse=index$array[recurse]=500$echo“数组[索引]=${数组[索引]},数组[递归]=${数组[递归]},数组[1]=${数组[1]}”数组[索引]=500,数组[递归]=500,array[1]=500可以看出,如果给index变量赋值1,修改array[index]的值,数组下标1对应的元素,即valuearray[1]的大小将被更改。也就是说,相当于用$index获取变量的值作为数组的下标。此时,由于没有给new变量赋值,所以修改array[new]的值仍然与array[0]有关,不会影响array[index]。如果给变量赋值的是一个字符串,那么就会递归得到该字符串对应的变量值。上面给"index"字符串赋值recurse,修改array[recurse]的值,可以看到array[1]的值变了。也就是说,相当于用$recurse获取递归变量的值作为“index”,如果发现是字符串,则继续使用“index”字符串作为变量名。使用$index获取index变量的值为1,最后使用1作为数组下标。
