在 Shell Script 中,預設情況下某個指令執行失敗(回傳非零的 exit code),腳本不會自動停止,而是繼續執行後面的指令。這常常導致後續操作在錯誤的狀態下繼續執行,產生難以追蹤的問題。
預設行為的問題
1
2
3
4
|
#!/bin/bash
cp important_file.txt /backup/
rm -rf /some/directory # 即使 cp 失敗,這行還是會執行
echo "Done"
|
如果 cp 失敗了,腳本仍然繼續執行 rm -rf,可能造成嚴重後果。
set -e:遇到錯誤立即退出
加上 set -e(等同於 set -o errexit)後,任何指令回傳非零 exit code,腳本就會立即停止執行:
1
2
3
4
5
6
|
#!/bin/bash
set -e
cp important_file.txt /backup/ # 若失敗,腳本在此停止
rm -rf /some/directory # 不會被執行
echo "Done"
|
set -e 的限制
set -e 並非萬能,以下情況不會觸發退出:
1
2
3
4
5
6
7
8
9
10
11
12
|
set -e
# if 條件中的指令失敗不會觸發 set -e
if false; then
echo "not reached"
fi
# && 或 || 連結的指令
false || true # 不會退出
# 被 ! 否定的指令
! false # 不會退出
|
set -u:未定義變數視為錯誤
預設情況下,使用未定義的變數會得到空字串,set -u(等同於 set -o nounset)讓這種情況直接報錯:
1
2
3
4
5
|
#!/bin/bash
set -u
echo $UNDEFINED_VAR # 沒有 set -u:印出空字串
# 有 set -u:報錯並停止
|
set -o pipefail:Pipe 中任一失敗就停止
set -e 只看最後一個指令的 exit code,在 pipe 中間失敗的指令不會觸發:
1
2
3
4
|
set -e
# 即使 cat nonexistent_file 失敗,因為 grep 成功,整行 exit code 為 0
cat nonexistent_file | grep "pattern"
|
加上 set -o pipefail 後,pipe 中任何一個指令失敗,整個 pipe 就算失敗:
1
2
3
4
|
set -e
set -o pipefail
cat nonexistent_file | grep "pattern" # 現在會因 cat 失敗而停止
|
完整的錯誤處理設定
1
2
3
4
5
|
#!/bin/bash
set -euo pipefail
# -e: 遇到錯誤立即退出
# -u: 未定義變數視為錯誤
# -o pipefail: pipe 中任一失敗就停止
|
trap ERR:錯誤時執行清理動作
trap 可以在腳本因錯誤退出時,執行指定的函式或指令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#!/bin/bash
set -euo pipefail
cleanup() {
local exit_code=$?
echo "腳本在第 $LINENO 行發生錯誤,exit code: $exit_code"
rm -f /tmp/temp_file.txt
}
trap cleanup ERR
echo "開始執行..."
cp important_file.txt /backup/
echo "完成!"
|
允許特定指令失敗
1
2
3
4
5
6
7
8
9
|
set -e
# 方法一:加上 || true
grep "pattern" file.txt || true
# 方法二:在 if 中使用
if ! some_command_that_might_fail; then
echo "指令失敗,但這是預期的"
fi
|
參考資料