Bash 陣列的進階使用技巧
在 Linux 系統管理與自動化腳本開發中,Bash 的陣列(Array)是一個極具潛力的工具。雖然許多初學者習慣使用變數來儲存單一值,但當我們需要處理一堆相關數據(例如:檔案列表、IP 地址、配置參數)時,陣列能讓腳本結構更清晰、執行效率更高。
這篇文章將跳過基礎的宣告方式,直接深入探討 Bash 陣列的進階技巧,包含索引操作、子字串切片、元素遍歷以及常見的陷阱與解決方案。
索引與切片:精準存取數據
Bash 支援兩種索引方式:基於零的數值索引(0, 1, 2...)和基於字串的關聯索引(類似其他語言的 Map 或 Dictionary)。對於系統管理員而言,數值索引最為常用。
假設我們有一個包含多個服務名稱的陣列,我們不僅可以取得全部元素,還能透過切片(Slicing)技術取得特定範圍的子陣列。
#!/bin/bash
# 定義一個服務列表陣列
services=("nginx" "mysql" "redis" "postgresql" "docker")
# 取得陣列長度
len=${#services[@]}
echo "總共有 $len 個服務"
# 取得第一個元素
echo "第一個服務: ${services[0]}"
# 取得最後一個元素(負數索引)
echo "最後一個服務: ${services[-1]}"
# 切片技巧:取得索引 1 到 2 的元素(mysql, redis)
# 格式為 ${陣列名[@]:起始索引:長度}
subset=("${services[@]:1:2}")
echo "中間的服務: ${subset[*]}"
這段程式碼展示了 ${#array[@]} 用於獲取長度,以及 ${array[@]:start:length} 的切片語法。特別注意的是,負數索引從陣列尾端開始計算,這在處理動態列表時非常有用。
遍歷與動態內容展開
在處理陣列時,for 迴圈是最常見的操作。然而,Bash 提供了兩種主要的展開方式:@ 和 *。它們的區別在於引數邊界的處理。
#!/bin/bash
# 定義 IP 地址陣列
ips=("192.168.1.1" "192.168.1.2" "10.0.0.1")
# 推薦方式:使用 "${array[@]}" 確保每個元素被視為獨立引數
# 這對於包含空格的元素尤其重要
for ip in "${ips[@]}"; do
echo "Ping 測試: $ip"
# 假設 ping 命令存在
# ping -c 1 -W 1 "$ip" > /dev/null 2>&1
done
# 錯誤示範:使用 "$*" 或 "$@" 未加引號
# for ip in $ips; do ... done
# 這會導致如果元素包含空格,會被錯誤分割
關鍵提醒:永遠使用雙引號將 "${array[@]}" 包起來。如果省略雙引號,Bash 會根據 IFS(內部欄位分隔符)對結果進行字詞分割,這會導致包含空格的路徑或名稱崩潰。
關聯陣列:鍵值對的強大應用
從 Bash 4.0 開始,引入了關聯陣列(Associative Arrays),這讓 Bash 擁有了類似 JSON 或 Python Dictionary 的能力。這在處理配置檔解析時極其強大。
#!/bin/bash
# 宣告關聯陣列
declare -A config
# 設定鍵值對
config["host"]="example.com"
config["port"]=8080
config["user"]="admin"
# 取得特定鍵的值
echo "Host: ${config[host]}"
echo "Port: ${config[port]}"
# 檢查鍵是否存在
if [[ -v config["password"] ]]; then
echo "Password is set."
else
echo "Password is not set."
fi
# 遍歷所有鍵
echo "All keys:"
for key in "${!config[@]}"; do
echo "$key -> ${config[$key]}"
done
這裡使用了 declare -A 來宣告關聯陣列。${!array[@]} 用於獲取所有鍵(keys),而 ${array[$key]} 用於獲取對應的值。-v 測試用於檢查鍵是否存在,避免存取未定義變數時的錯誤。
常見問題與解決方案
1. 陣列元素包含空格怎麼辦?
這是 Bash 新手最常遇到的陷阱。如果陣列元素包含空格(例如檔案路徑 /home/user/my document.txt),在遍歷時必須嚴格使用 "${array[@]}"。
#!/bin/bash
files=("file 1.txt" "file 2.txt")
# 正確:每個元素作為獨立引數傳遞
for f in "${files[@]}"; do
echo "處理檔案: [$f]" # 輸出: 處理檔案: [file 1.txt]
done
# 錯誤:未加引號,空格會導致變數被分割
for f in ${files[@]}; do
echo "處理檔案: [$f]" # 輸出: 處理檔案: [file] 然後 處理檔案: [1.txt]
done
2. 如何合併兩個陣列?
Bash 沒有內建的 array1 + array2 運算子,但我們可以透過陣列展開輕鬆合併。
#!/bin/bash
arr1=("a" "b")
arr2=("c" "d")
# 合併方法:將兩個陣列展開並賦值給新陣列
merged=("${arr1[@]}" "${arr2[@]}")
echo "合併後: ${merged[*]}" # 輸出: a b c d
小結
Bash 陣列不僅僅是儲存列表的工具,透過切片、關聯陣列以及正確的引數展開技巧,我們能寫出更健壯、更易維護的腳本。記住以下三個黃金法則:
- 始終使用
"${array[@]}"進行遍歷或傳遞引數。 - 善用負數索引存取尾部元素。
- 需要鍵值對映射時,無腦使用
declare -A。
掌握這些技巧,你的 Shell 腳本將能應對更複雜的系統管理任務。