軌道
/
Bash
Bash
/
課程大綱
/
更多關於陣列
更多

更多關於陣列 在 Bash

{other: "%{count} 個練習"}

關於 更多關於陣列

我們在陣列這一章介紹過陣列。 這份文件會介紹更多使用陣列的方式。

將陣列元素串接成單一字串

在之前的陣列章節中,你看到了"${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"
Advanced
我們可以把這段邏輯封裝成一個函式。 點擊查看詳細內容。
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 "$*"
}
如果不使用函式,在子 shell 中修改 IFS 是避免動到目前 shell 的好方法。 點擊查看詳細內容。
(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 也能用於關聯式陣列,以及「純量」變數(也就是存放字串值的變數)。

Note

在函式內,declare -p local_array幫助不大。 它只會輸出declare -n local_array="array_original"。 你可以透過檢查傳入的陣列名稱來取得陣列的詳細資訊:declare -p "$1"

Caution

請留意,區域陣列的名稱要和傳入的陣列名稱不同。 程式仍然可以運作,但會產生像這樣的「循環名稱參照」警告:

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 腳本,你就會用位置參數來存放一「陣列」的值。

透過 GitHub 編輯 連結會在新視窗或分頁中開啟