Shell 腳本的流程控制結構 (條件判斷、迴圈、分支選擇) 是自動化運維、資料處理、部署管線的核心骨架。本文系統整理 if/elif/else/fi、for/while/until、case/esac、select 完整語法、實戰範例、常見陷阱與現代最佳實踐 (Bash 4/5 新特性)。
if [[ condition ]]; then
action
elif [[ condition ]]; then
action
else
action
fi
關鍵規則:[[ ... ]] 為 Bash 擴展測試命令,**優於單括號 [ ... ]** (支援 &&/||/</>/=~ 正則、變數無需雙引號)。
| 類別 | 運算子 | 說明 | 範例 |
|---|---|---|---|
| 檔案測試 | -e | 存在 | [[ -e file ]] |
-f | 一般檔案 | [[ -f file ]] | |
-d | 目錄 | [[ -d dir ]] | |
-r/-w/-x | 可讀/寫/執行 | [[ -x script.sh ]] | |
-s | 大小 > 0 | [[ -s log.txt ]] | |
-L | 符號連結 | [[ -L link ]] | |
| 字串測試 | -z | 長度為 0 | [[ -z "$var" ]] |
-n | 長度非 0 | [[ -n "$var" ]] | |
==/!= | 相等/不等 (支援 glob 模式) | [[ "$str" == *.txt ]] | |
=~ | 正則比對 (Bash 3.2+) | [[ "$email" =~ ^[^@]+@[^@]+\.[^@]+$ ]] | |
</> | 字典序比較 | [[ "a" < "b" ]] | |
| 整數比較 | -eq/-ne | 等於/不等於 | [[ $a -eq 10 ]] |
-lt/-le | 小於/小於等於 | [[ $a -lt 100 ]] | |
-gt/-ge | 大於/大於等於 | [[ $a -gt 0 ]] | |
(()) | 算術語境 (支援 < > <= >= == !=) | (( a > 10 )) | |
| 邏輯組合 | &&/||/! | 且/或/非 (僅 [[ ]] 與 (( ))) | [[ -f $f && -r $f ]] |
#!/bin/bash
score=78
if (( score >= 90 )); then
echo "優等"
elif (( score >= 80 )); then
echo "甲等"
elif (( score >= 70 )); then
echo "乙等"
elif (( score >= 60 )); then
echo "及格"
else
echo "不及格"
fi
# 形式 A: for 變數 in 列表 (最常用)
for i in {1..5}; do
echo $i
done
# 形式 B: C 風格算術 for (( ; ; ))
for ((i=1; i<=5; i++)); do
echo $i
done
# 形式 C: 遍歷陣列/命令輸出
files=(/var/log/*.log)
for f in "${files[@]}"; do
echo "Processing $f"
done
# 計數器
i=10
while (( i > 5 )); do
echo $i
((i--))
done
# 逐行讀取檔案 (標準模式,保留前導/尾隨空白)
while IFS= read -r line; do
echo "Line: $line"
done < /etc/hosts
# 讀取命令輸出 (避免 subshell 變數作用域問題)
find /tmp -name "*.tmp" -print0 |
while IFS= read -r -d '' file; do
rm -f "$file"
done
a=10
until (( a < 0 )); do
echo $a
((a--))
done
# 輸出 10 9 8 7 6 5 4 3 2 1 0
for i in {1..10}; do
(( i == 5 )) && continue # 跳過 5
(( i == 8 )) && break # 8 時結束
echo $i
done
# 輸出 1 2 3 4 6 7
case $1 in
start|begin)
echo "啟動服務"
;;
stop|end)
echo "停止服務"
;;
restart|reload)
echo "重啟服務"
;;
status)
systemctl status myservice
;;
*)
echo "用法: $0 {start|stop|restart|status}"
exit 1
;;
esac
模式支援:* (任意字串)、? (單字元)、[abc] (字元類)、[a-z] (範圍)、| (多選)。
# Bash 4+ 支援
case $var in
pattern1) action1 ;;& # 繼續測試下一模式
pattern2) action2 ;& # 執行下一動作不再測試
*) default_action ;;
esac
PS3="請選擇操作: "
options=("啟動" "停止" "重啟" "狀態" "離開")
select opt in "${options[@]}"; do
case $opt in
"啟動") systemctl start myservice ;;
"停止") systemctl stop myservice ;;
"重啟") systemctl restart myservice ;;
"狀態") systemctl status myservice ;;
"離開") echo "再見"; break ;;
*) echo "無效選項 $REPLY" ;;
esac
done
select 內建無限迴圈,需配合 break 退出。
[[ ]] 和 (( )),避免 [ ] 詞語分割與檔名展開 Bug。"$var",除非刻意要詞語分割。while IFS= read -r line,保留空白與反斜線。while ... done < <(cmd) (Process Substitution) 或 mapfile。case 替代長鏈 if/elif,可讀性與效能更佳。set -euo pipefail (腳本開頭),嚴格錯誤處理。
# 1. 語法檢查
bash -n script.sh
# 2. 逐行追蹤執行
bash -x script.sh
# 或腳本內動態開關
set -x # 開啟追蹤
set +x # 關閉追蹤
# 3. 列印變數值
echo "DEBUG: var=$var" >&2
# 4. ShellCheck 靜態分析 (強烈推薦)
shellcheck script.sh
掌握 Shell 流程控制的完整語法、模式匹配威力、現代最佳實踐與除錯工具,能讓自動化腳本從「能跑」進化為「健壯、可維護、可測試」的生產級代碼資產。