Shell Script 是什麼#

Shell Script 是將一連串的 Shell 指令寫在純文字檔案中,讓系統依序執行。它不需要編譯,直接由 Shell 直譯執行。你可以把它想成錄製巨集——將你在終端中手動輸入的操作自動化。

為什麼 Shell Script 重要#

  • 系統管理自動化:開機服務、定期備份、日誌清理
  • 批次操作:對大量檔案或目錄進行統一處理
  • CI/CD Pipeline:幾乎所有的 CI/CD 系統底層都在跑 shell script
  • 快速原型開發:不需要安裝任何開發環境,任何 Linux 系統都能跑

Shell Script 的優勢在於「黏合」——串接各種指令和工具。它不適合處理複雜的資料結構或高效能運算。當 script 超過幾百行或邏輯變得很複雜時,應考慮改用 Python、Go 等語言。

Script 的基本結構#

一個良好的 Shell Script 包含以下部分:

  1. Shebang 宣告:第一行的 #!/bin/bash,告訴系統用哪個 Shell 來執行
  2. 說明註解:腳本用途、作者、版本、修改記錄
  3. 環境變數設定:確保腳本在預期的環境中執行
  4. 主程式邏輯
  5. 回傳值:用 exit 指定回傳碼

執行方式的差異#

Shell Script 的執行方式會影響變數的作用範圍:

  • 直接執行./script.shbash script.sh):在子程序中執行,script 中設定的變數不會影響父程序
  • source script.sh:在目前的 Shell 中執行,變數會保留在目前環境中

這就是為什麼修改了 ~/.bashrc 後要執行 source ~/.bashrc——你需要在目前的 Shell 中重新載入設定,而不是在子程序中執行它。

判斷式#

test 指令與 [ ]#

Shell 的判斷不是用 if (condition) 這種一般程式語言的語法,而是透過 test 指令或其等價的 [ ] 中括號語法。

判斷的類型:

  • 檔案測試:檔案是否存在、是否為目錄、是否可讀寫執行
  • 數值比較-eq-ne-gt-lt-ge-le
  • 字串比較=!=-z(空字串)、-n(非空字串)

[ ] 中括號內的每個元素必須用空格分隔,包括括號本身。[$var = "hello"] 是錯誤的,正確寫法是 [ "$var" = "hello" ]。變數也要用雙引號包裹,避免變數為空時造成語法錯誤。

預設變數#

Shell Script 接收參數的方式:

變數意義
$0Script 本身的檔名
$1 ~ $N第 1 ~ 第 N 個參數
$#參數個數
$@所有參數(各自獨立)
$*所有參數(合為一個字串)

條件判斷與流程控制#

if…then…fi#

基本的條件分支結構。elif 用於多重條件判斷。

case…esac#

類似其他語言的 switch-case,適合用在固定選項的判斷(如命令列選項的解析)。

function#

Shell Script 支援函式定義,用於模組化重複使用的邏輯。函式的參數同樣使用 $1$2…,注意這些參數是函式的區域參數,不是 script 的全域參數。

迴圈#

while / until#

  • while:條件成立時持續執行
  • until:條件不成立時持續執行(while 的相反)

for#

兩種形式:

  • 清單式for var in list; do ... done——遍歷清單中的每個值
  • 數值式for ((i=0; i<10; i++)); do ... done——類似 C 語言的語法

除錯技巧#

Shell Script 的除錯不像程式語言有完整的 debugger,但有幾個實用的方法:

  • bash -n script.sh語法檢查,不實際執行
  • bash -v script.sh:執行前顯示每一行原始碼
  • bash -x script.sh:執行時顯示每個指令展開後的內容(最常用)

bash -x 是 Shell Script 除錯的救命工具。它會在每個指令前顯示 + 號,並展開所有變數的值,讓你清楚看到實際執行的是什麼。在 CI/CD 環境中,開啟 set -x 能幫助快速定位腳本執行失敗的位置。

撰寫良好 Script 的原則#

  1. 總是加上 Shebang:不要假設執行環境的預設 Shell
  2. 使用 set -euo pipefail:在 script 開頭加上這行,讓錯誤即時中止、未定義變數報錯、管線中的錯誤被捕捉
  3. 變數加引號"$variable" 而不是 $variable,避免空值或空格造成的問題
  4. 使用有意義的回傳碼:成功回傳 0,不同的失敗情況回傳不同的非零值
  5. 寫註解:解釋「為什麼」而不是「做什麼」