Bash 陣列的進階使用技巧 文章首圖

Bash 陣列的進階使用技巧

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 陣列不僅僅是儲存列表的工具,透過切片、關聯陣列以及正確的引數展開技巧,我們能寫出更健壯、更易維護的腳本。記住以下三個黃金法則:

  1. 始終使用 "${array[@]}" 進行遍歷或傳遞引數。
  2. 善用負數索引存取尾部元素。
  3. 需要鍵值對映射時,無腦使用 declare -A

掌握這些技巧,你的 Shell 腳本將能應對更複雜的系統管理任務。