我們在陣列這一章介紹過陣列。 這份文件會介紹更多使用陣列的方式。
在之前的陣列章節中,你看到了"${myarray[@]}",搭配@索引,可以把陣列展開成一個個元素。
但有時候你會想把所有元素串接成單一字串。
這時請使用*索引:
echo "${myarray[*]}"
這個展開必須用雙引號包起來。
Bash 會把IFS這個內建變數的_第一個字元_當作分隔字元。
預設情況下,$IFS包含空格、定位字元和換行字元。
myarray=(one two three four)
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one two three four"
我們可以調整IFS變數,改用不同的分隔字元:
myarray=(one two three four)
IFS=","
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one,two,three,four"
join() {
local IFS=$1
shift
local elements=("$@")
echo "${elements[*]}"
}
join ":" "${myarray[@]}" # note, the "@" index
# => "one:two:three:four"
在函式中把IFS區域化,代表我們不必儲存舊值,之後再到全域作用域中把它還原成先前的值。
更精簡一點的做法是,特殊參數"$*"加上引號後具有同樣的功能,所以我們不需要額外保存一份函式的引數:
join() {
local IFS=$1
shift
echo "$*"
}
(IFS=","; echo "${myarray[*]}")
括號會建立一個子 shell(目前 shell 的副本)。括號內的指令執行完畢後,子 shell 就會結束,被修改的 IFS 變數也隨之消失。
請注意,這樣寫不會成功:IFS="," echo "${myarray[*]}"。參數展開會_先_執行完畢,然後 shell 才把修改後的 IFS 變數套用到echo指令。
你可能看過"${variable:offset:length}"這個參數展開,它會展開成變數值的_子字串_。
我們也可以用同樣的方式處理陣列,展開陣列中的某個切片。
myarray=(one two three four)
subarray=("${myarray[@]:0:2}")
declare -p subarray
# => declare -a subarray=([0]="one" [1]="two")
subarray=("${myarray[@]:1:3}")
declare -p subarray
# => declare -a subarray=([0]="two" [1]="three" [2]="four")
省略長度部分,代表「從位移處到陣列結尾」:
subarray=("${myarray[@]:2}")
declare -p subarray
# => declare -a subarray=([0]="three" [1]="four")
這並不像你可能熟悉的其他語言那麼直接。 把陣列傳給函式主要有兩種技巧。
第一種技巧是,你把陣列所有的值傳進去,並在函式中把它們蒐集成一個區域陣列。
myfunc() {
local array_copy=("$@")
# do stuff with array_copy
declare -p array_copy
}
array_original=(11 22 33 44)
myfunc "${array_original[@]}"
函式中的陣列存放的是這些值的_副本_。 在函式中對陣列所做的任何修改,都不會反映到外層作用域。
這種技巧比較接近你可能在其他語言看過的「傳參考」功能。 你把陣列的_名稱_以字串的形式傳進去。 函式會建立一個帶有「nameref」屬性的區域變數。 這個區域陣列和全域陣列(也就是我們傳入名稱的那個)是_同一個陣列_。
myfunc() {
# note the `-n` option
local -n local_array=$1
# do stuff with local_array
for i in "${!local_array[@]}"; do
printf '%d => %s\n' "$i" "${local_array[i]}"
end
# we can mutate it
local_array+=(55 66 77)
}
array_original=(11 22 33 44)
myfunc "array_original"
# show the mutated array
declare -p array_original
# => declare -a array_original=([0]="11" [1]="22" [2]="33" [3]="44" [4]="55" [5]="66" [6]="77")
Nameref 也能用於關聯式陣列,以及「純量」變數(也就是存放字串值的變數)。
在函式內,declare -p local_array幫助不大。
它只會輸出declare -n local_array="array_original"。
你可以透過檢查傳入的陣列名稱來取得陣列的詳細資訊:declare -p "$1"
請留意,區域陣列的名稱要和傳入的陣列名稱不同。 程式仍然可以運作,但會產生像這樣的「循環名稱參照」警告:
myfunc() {
local -n a=$1
local IFS=,
echo "${a[*]}"
}
# same name as the function's local variable
a=(one two three)
myfunc a
bash: local: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
one,two,three
在只以符合 POSIX 標準為目標的 shell 中(例如ash和dash),並沒有陣列。
最接近的做法就是使用位置參數。
$1、$2等等。"$@"可以把它們展開成一個個元素。"$*"可以把它們串接成單一字串。$#。使用set指令來指定它們的值:
set -- one two three
set -- "$@" four
for item in "$@"; do
echo "do something with $item"
done
如果你的目標是寫出「可攜」的 shell 腳本,你就會用位置參數來存放一「陣列」的值。