Bash 陷阱與常見錯誤的解決方法 文章首圖

Bash 陷阱與常見錯誤的解決方法

Bash 陷阱與常見錯誤的解決方法

Shell 腳本雖然是 Linux 系統管理的利器,但對於初學者甚至經驗豐富的開發者來說,Bash 的語法特性往往潛藏著許多不易察覺的陷阱。一個小小的引號遺漏或變數展開錯誤,就可能導致資料遺失或資安漏洞。本文將針對 Ubuntu 22.04 與 Debian 12 環境,剖析幾個最常見的 Bash 陷阱,並提供實用的解決方案與完整範例。

陷阱一:未加引號的變數展開導致字詞拆分

這是 Bash 新手最常犯錯的地方。當變數內容包含空格時,若未使用雙引號包圍,Bash 會根據內部欄位分隔符號(IFS,預設為空格、跳脫字元、換行符號)將變數拆分成多個參數,進而導致指令行為異常。

錯誤示範: 假設我們有一個檔案路徑包含空格:

filename="my document.txt"
# 危險!Bash 會將其視為兩個參數:my 和 document.txt
cp $filename /tmp/

上述指令實際上執行的是 cp my document.txt /tmp/,這通常會導致 cp 指令報錯,因為它誤以為要複製檔案 my 到目錄 document.txt,而 document.txt 被視為額外參數或目標目錄。

正確做法: 務必使用雙引號將變數包起來,強制 Bash 將其視為單一參數:

filename="my document.txt"
# 正確!變數內容保持完整
cp "$filename" /tmp/

陷阱二:判斷式中的邏輯運算符誤用

在 Bash 的 [ (test) 或 [[ 指令中,邏輯「與」和「或」的寫法與 C 語言或 Python 不同。許多使用者習慣使用 &&|| 作為判斷條件內的邏輯運算,這在 [ 中是無效的,甚至會導致語法錯誤。

錯誤示範:

age=25
# 錯誤!在 [ ] 內使用 && 會導致語法錯誤或意外行為
if [ $age -gt 18 && $age -lt 60 ]; then
    echo "Adult"
fi

正確做法: 使用 -a (and) 和 -o (or) 運算符,或者更推薦使用 [[ ]] 並結合 &&||,因為 [[ ]] 提供了更強大的模式匹配與邏輯運算支援:

age=25
# 推薦寫法:使用 [[ ]] 結合 &&
if [[ $age -gt 18 && $age -lt 60 ]]; then
    echo "Adult"
fi

# 傳統寫法:使用 [ ] 結合 -a
if [ $age -gt 18 -a $age -lt 60 ]; then
    echo "Adult"
fi

實用範例:安全處理檔案路徑與條件判斷

以下是一個完整可執行的 Bash 腳本,示範如何安全地處理包含空格的檔案路徑,並結合正確的條件判斷邏輯。此腳本會檢查指定目錄是否存在,並列出其中所有 .txt 檔案的大小。

#!/bin/bash

# 設定目標目錄,包含空格以測試變數展開安全性
target_dir="/tmp/my test dir"
txt_pattern="*.txt"

# 1. 檢查目錄是否存在(使用 [[ ]] 確保安全性)
if [[ -d "$target_dir" ]]; then
    echo "目錄存在:$target_dir"
else
    echo "目錄不存在,正在建立..."
    mkdir -p "$target_dir"
fi

# 2. 在目錄中尋找 .txt 檔案
# 注意:使用 find 指令並搭配 -print0 處理特殊字元,避免管道錯誤
file_count=$(find "$target_dir" -maxdepth 1 -type f -name "$txt_pattern" | wc -l)

# 3. 條件判斷:檔案數量大於 0 且小於 10
if [[ $file_count -gt 0 && $file_count -lt 10 ]]; then
    echo "找到 $file_count 個 .txt 檔案:"

    # 使用 while read 迴圈安全處理每行路徑
    find "$target_dir" -maxdepth 1 -type f -name "$txt_pattern" -print0 | while IFS= read -r -d '' file; do
        # 獲取檔案大小(單位:位元組)
        file_size=$(stat -c %s "$file")
        # 獲取檔案修改時間
        file_mtime=$(stat -c %y "$file")

        echo "  - 檔案:$file"
        echo "    大小:${file_size} bytes"
        echo "    修改時間:$file_mtime"
    done
else
    echo "檔案數量不符合條件 ($file_count)"
fi

echo "腳本執行完畢。"

常見問題與解法

問題一:腳本執行時出現「Permission denied」

原因: Bash 腳本預設不會自動賦予執行權限。 解法: 使用 chmod 賦予執行權限:

chmod +x script.sh
./script.sh

問題二:變數內容出現意外換行或空格被截斷

原因: 未正確設定 IFS 或使用 read 指令時未指定 -r 參數。 解法: 在使用 read 讀取檔案內容時,務必加上 -r 以避免反斜線被解譯,並使用 -d '' 配合 find -print0 處理包含換行符號的檔名:

# 正確讀取含特殊字元的檔名
find . -type f -print0 | while IFS= read -r -d '' file; do
    echo "$file"
done

小結

Bash 腳本的強大之處在於其靈活性,但這種靈活性也伴隨著潛在的風險。透過始終使用雙引號包圍變數、正確使用 [[ ]] 進行條件判斷,以及善用 find 配合 -print0 處理特殊檔案名,可以大幅減少腳本執行時的錯誤。建議在撰寫複雜腳本時,使用 bash -n script.sh 進行語法檢查,並在測試環境中充分驗證,以確保腳本在 Ubuntu 22.04 或 Debian 12 等現代 Linux 發行版上的穩定性與安全性。