Bash 的關(guān)聯(lián)數(shù)組詳解
Bash 支持關(guān)聯(lián)數(shù)組(associative arrays),可以使用任意的字符串、或者整數(shù)作為下標(biāo)來訪問數(shù)組元素。 關(guān)聯(lián)數(shù)組的下標(biāo)和值稱為鍵值對(duì),它們是一一對(duì)應(yīng)關(guān)系,鍵是唯一的,值可以不唯一。
要使用關(guān)聯(lián)數(shù)組之前,需要用 declare -A array_name 來進(jìn)行顯式聲明 array_name 變量為關(guān)聯(lián)數(shù)組。
查看 help declare 對(duì) -A 選項(xiàng)的說明如下:
-A to make NAMEs associative arrays (if supported)
例如下面的語句定義了一個(gè)名為 filetypes 的關(guān)聯(lián)數(shù)組,并為數(shù)組賦值:
$ declare -A filetypes=([txt]=text [sh]=shell [mk]=makefile)
$ filetypes[c]="c source file"
在使用數(shù)組名進(jìn)行賦值時(shí),需要用小括號(hào) () 把所有的值括起來。
在關(guān)聯(lián)數(shù)組里面,用方括號(hào) [] 括起來的值是 key。 為方括號(hào) [] 賦予的值是該 key 對(duì)應(yīng)的 value。 不同的鍵值對(duì)之間用空格隔開。注意不是用逗號(hào)隔開。
也可以使用 filetypes[key]=value 的方式單獨(dú)為指定的關(guān)聯(lián)數(shù)組元素賦值。 如果所給的 key 之前不存在,bash 會(huì)自動(dòng)創(chuàng)建它。 如果已經(jīng)存在,則修改它的值為 value 對(duì)應(yīng)的值。
基于前面定義的 filetypes 這個(gè)數(shù)組名:
-
${!filetypes[*]} :獲取關(guān)聯(lián)數(shù)組的所有鍵名,注意在 filetypes 前面有一個(gè)感嘆號(hào) ‘!’。
$ echo ${!filetypes[*]}
txt sh c mk
-
${!filetypes[@]} : 獲取關(guān)聯(lián)數(shù)組的所有鍵名。后面會(huì)說明使用 * 和 @ 的區(qū)別。
$ echo ${!filetypes[@]}
txt sh c mk
-
${filetypes[*]} :獲取關(guān)聯(lián)數(shù)組的所有值。相比于獲取鍵名的表達(dá)式,少了前面的感嘆號(hào) ‘!’。
$ echo ${filetypes[*]}
text shell c source file makefile
-
${filetypes[@]} :獲取關(guān)聯(lián)數(shù)組的所有值。
$ echo ${filetypes[@]}
text shell c source file makefile
-
${#filetypes[*]} :獲取關(guān)聯(lián)數(shù)組的長度,即元素個(gè)數(shù)。注意在 filetypes 前面有一個(gè)井號(hào) ‘#’。
$ echo ${#filetypes[*]}
4
-
${#filetypes[@]} :獲取關(guān)聯(lián)數(shù)組的長度,即元素個(gè)數(shù)
$ echo ${#filetypes[@]}
4
-
${filetypes[key]} :獲取 key 這個(gè)鍵名對(duì)應(yīng)的值。注意大括號(hào) {} 是必須的。
$ echo ${filetypes[sh]}
shell
$ echo $filetypes[sh]
[sh] # 可以看到,不加大括號(hào)時(shí),并不能獲取到數(shù)組元素的值
查看 man bash 的 Arrays 小節(jié),說明了這幾個(gè)表達(dá)式的含義,同時(shí)還提到使用 * 和 @ 的區(qū)別,貼出具體的區(qū)別如下:
If the word is double-quoted, ${name[*]} expands to a single word with the value of each array member separated by the first character of the IFS special variable, and ${name[@]} expands each element of name to a separate word. When there are no array members, ${name[@]} expands to nothing.${!name[@]} and ${!name[*]} expand to the indices assigned in array variable name. The treatment when in double quotes is similar to the expansion of the special parameters @ and * within double quotes.
即,使用 * 時(shí),如果用雙引號(hào)把整個(gè)表達(dá)式括起來,例如寫為 "${!name[*]}" 、或者 "${name[*]}" ,那么會(huì)把所有值合并成一個(gè)字符串。
使用 @ 時(shí),如果用雙引號(hào)把整個(gè)表達(dá)式括起來,例如寫為 "${!name[@]}" 、或者 "${name[@]}" ,那么會(huì)得到一個(gè)字符串?dāng)?shù)組。 每個(gè)數(shù)組元素會(huì)用雙引號(hào)括起來,所以數(shù)組元素自身的空格不會(huì)導(dǎo)致拆分成幾個(gè)單詞。
具體如下面的例子所示,這也是遍歷數(shù)組元素的例子:
$ for key in "${filetypes[*]}"; do echo "****:" $key; done
****: text shell c source file makefile
$ for key in "${filetypes[@]}"; do echo "@@@@:" $key; done
@@@@: text
@@@@: shell
@@@@: c source file
@@@@: makefile
可以看到,"${filetypes[*]}" 只產(chǎn)生一個(gè)字符串,for 循環(huán)只遍歷一次。 而 "${filetypes[@]}" 產(chǎn)生了多個(gè)字符串,for 循環(huán)遍歷多次,是一個(gè)字符串?dāng)?shù)組。 而且所給的 "c source file" 這個(gè)字符串沒有被空格隔開成幾個(gè)單詞。
上面的例子也演示了如何用 for 命令來遍歷數(shù)組元素。
可以使用 declare -p 命令來查看數(shù)組具體的鍵值對(duì)關(guān)系:
$ declare -p filetypes
declare -A filetypes='([txt]="text" [sh]="shell" [c]="c source file" [mk]="makefile" )'
Bash 的一維數(shù)組詳解
Bash 只支持一維數(shù)組 (one-dimensional indexed array),不支持二維數(shù)組。 聲明一維數(shù)組的方式是:declare -a array_name 。 由于 bash 不要求明確指定變量的類型,其實(shí)不聲明也可以,按數(shù)組的方式直接賦值給變量即可。
查看 help declare 對(duì) -a 選項(xiàng)的說明如下:
-a to make NAMEs indexed arrays (if supported)
使用 declare -a 聲明的數(shù)組,默認(rèn)以數(shù)字作為數(shù)組下標(biāo),而且不需要指定數(shù)組長度。 其賦值方式說明如下:
-
array=(value1 value2 value3 ... valueN):這種方式從數(shù)組下標(biāo) 0 開始為數(shù)組元素賦值,不同值之間用空格隔開,所給的值可以是數(shù)字、字符串等。
$ declare -a array=(1 2 "30" "40" 5)
$ echo ${array[@]}
1 2 30 40 5
-
array=([0]=var1 [1]=var2 [2]=var3 ... [n]=varN):這種方式顯式提供數(shù)組下標(biāo),指定為該元素賦值,所給的數(shù)組下標(biāo)可以不連續(xù)。
$ declare -a array=([0]=1 [1]=2 [3]="30" [6]="60" [9]=9)
$ echo ${array[@]} # 用 ${array[@]} 獲取所有數(shù)組元素的值
1 2 30 60 9
$ echo ${array[5]} # 上面賦值的時(shí)候,跳過了數(shù)組下標(biāo) 5,所以它對(duì)應(yīng)的值為空
$ declare -p array # 使用 declare -p 命令查看,會(huì)打印出被賦值的所有元素
declare -a array='([0]="1" [1]="2" [3]="30" [6]="60" [9]="9")'
-
array[0]=value1; array[1]=value2; ...; array[n]=varN:這種方式是單獨(dú)為數(shù)組元素賦值。
$ unset array; declare -a array
$ array[0]=0; array[1]=1; array[7]="70"
$ declare -p array
declare -a array='([0]="0" [1]="1" [7]="70")'
一維數(shù)組的其他用法和前面文章介紹的關(guān)聯(lián)數(shù)組用法一樣。 例如,可以用 ${array[@]} 獲取所有數(shù)組元素的值,用 ${#array[@]} 獲取數(shù)組的元素個(gè)數(shù),等等。
可以參考下面的代碼片段來遍歷一維數(shù)組元素:
for item in "${array[@]}"; do
echo $item
done
一維數(shù)組通過正整數(shù)來索引數(shù)組元素。 如果提供負(fù)整數(shù)的下標(biāo)值,那么它具有特殊含義,表示從數(shù)組末尾開始往前索引。 例如,array[-1] 會(huì)索引到數(shù)組的最后一個(gè)元素,array[-2] 索引到數(shù)組的倒數(shù)第二個(gè)元素,依此類推。
具體舉例說明如下:
$ declare -a array=([0]=0 [1]=1 [2]="20" [3]=3)
$ echo ${array[-1]}, ${array[-3]}
3, 1
注意:雖然 declare -a 聲明的數(shù)組要用數(shù)字作為數(shù)組下標(biāo),但是使用字符串作為數(shù)組下標(biāo)并不會(huì)報(bào)錯(cuò)。 實(shí)際測(cè)試有一些比較古怪的地方。具體舉例如下:
$ declare -a array=([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 -p array
declare -a array='([0]="2000" [1]="1" [2]="20" [3]="3")'
可以看到,為 array[index] 元素賦值,沒有報(bào)錯(cuò),使用 ${array[index]} 可以正常獲取到它的值。 但是為 array[new] 賦值為 2000 后,使用 ${array[index]} 打印 index 這個(gè)字符串下標(biāo)對(duì)應(yīng)的數(shù)組元素值,發(fā)現(xiàn)變成了 2000,跟 ${array[new]} 打印的值一樣。 看起來,就像是這兩個(gè)字符串下標(biāo)關(guān)連到同一個(gè)數(shù)組元素。
實(shí)際上,它們都對(duì)應(yīng)到數(shù)組元素 0。可以看到,上面的 declare -p array 命令打印出 [0] 這個(gè)元素值變成了 2000。
查看 man bash 的 Arrays 部分,說明如下:
Indexed arrays are referenced using integers (including arithmetic expressions) and are zero-based;An indexed array is created automatically if any variable is assigned to using the syntax name[subscript]=value. The subscript is treated as an arithmetic expression that must evaluate to a number.
Referencing an array variable without a subscript is equivalent to referencing the array with a subscript of 0.
即,indexed array 的下標(biāo)一定是數(shù)字、或者是經(jīng)過算術(shù)表達(dá)式 (arithmetic expressions) 計(jì)算得到的數(shù)字。 如果沒有提供數(shù)組下標(biāo),默認(rèn)會(huì)使用數(shù)組下標(biāo) 0。
由于 bash 的算術(shù)表達(dá)式在獲取變量值時(shí),不需要使用 $ 符號(hào),所以上面的 array[index] 實(shí)際上相當(dāng)于 array[$index] ,也就是獲取 index 變量的值來作為數(shù)組下標(biāo)。
如果所給的 index 變量沒有值,就相當(dāng)于沒有提供數(shù)組下標(biāo),默認(rèn)使用數(shù)組下標(biāo) 0,所以為 array[index] 賦值,實(shí)際上是為 array[0] 賦值。 同理,為 array[new] 賦值,也是為 array[0] 賦值,會(huì)看到 array[index] 的值也跟著改變。
如果 index 變量的值不是 0,而且 new 變量沒有值,那么為 array[index] 賦值,將不會(huì)影響到 array[new] 。
在上面例子的基礎(chǔ)上,繼續(xù)執(zhí)行下面語句:
$ 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 "array[index] = ${array[index]}, array[recurse] = ${array[recurse]}, array[1] = ${array[1]}"
array[index] = 500, array[recurse] = 500, array[1] = 500
可以看到,將 index 變量賦值為 1,修改 array[index] 的值,則改變的是數(shù)組下標(biāo) 1 對(duì)應(yīng)的元素、也就是 array[1] 的值。 即相當(dāng)于用 $index 獲取該變量的值來作為數(shù)組下標(biāo)。 此時(shí),由于沒有為 new 變量賦值,修改 array[new] 的值還是關(guān)連到 array[0] ,不會(huì)影響到 array[index] 。
如果將變量賦值為字符串,那么會(huì)往下遞歸獲取該字符串對(duì)應(yīng)的變量值。 上面將 recurse 賦值為 "index" 字符串,修改 array[recurse] 的值,可以看到 array[1] 的值被改變了。 即相當(dāng)于先用 $recurse 獲取 recurse 變量的值是 "index",發(fā)現(xiàn)是字符串,繼續(xù)把 "index" 字符串作為變量名。 用 $index 來獲取 index 變量的值是 1,最終使用 1 作為數(shù)組下標(biāo)。
|