Kurt/Shell Script Error Handling

Created Thu, 07 Jul 2022 13:54:31 +0800 Modified Wed, 02 Nov 2022 05:33:26 +0000
1368 Words 6 min

Shell script 錯誤處理(Error Handling)

因最近工作需求,需要撰寫一些腳本(scripts),以往寫的腳本都偏簡單,沒有什麼複雜的情況,更不用說如果執行失敗,還要做什麼錯誤處理 xD

不過這次遇到了需要處理的情況,上網查了一些資料,除了自行做紀錄以外,也分享給大家

大家除了看完文章以外,也歡迎大家把腳本片段複製下來,自己實際去執行看看結果!

特殊參數 $?

$? 可以拿到最後一個指令的返回碼,只有 0 帶表示執行成功,狀態碼請參考下表:

狀態碼 說明
0 指令成功
1 所有一般未知的錯誤
2 錯誤的指令
126 指令無法執行
127 指令不存在
128 無效的退出
128+n 與 linux 信號相關的嚴重錯誤
130 使用 Ctrl+c 結束
255* 超出範圍的狀態碼

所以我們只要在 「可能」 會出錯的指令後面去判斷 $? 的值是多少,就可以在這邊去決定我們要做什麼錯誤處理

以下腳本因為 a.txt 不存在,所以執行 cp a.txt b.txt 時就出錯了,因此下一行使用 echo 印出 $? 的結果就不是 0

#!bin/bash

# 實際上不存在 a.txt 檔案,所以這邊會出錯
cp a.txt b.txt

# 印出來的結果的確不是 0
echo $?

Example 1

所以我們就可以利用 $? 結果不等於 0 的情況下,寫個 if else 去做錯誤處理

#!bin/bash

# 實際上不存在 a.txt 檔案,所以這邊會出錯
cp a.txt b.txt
# 先把上一個指令的結果碼用變數記下來,因為當你執行下一個指令時

# $? 的結果就會變成下一個指令的結果碼
lastResult=$?

# 如果結果不等於0,就印出 "執行失敗"
if [ $lastResult -ne 0 ]
then
    echo "執行失敗"
fi

Exapmle 2

每個指令都要判斷,好麻煩 ⁉️

因為我們的 shell script 通常不會只有兩三行指令,隨著要做的事情增加,我們的 script 也會隨之變得龐大,如果我們需要在每個指令後面加上 if else 判斷是否成功,以及失敗後是否要中斷執行,這樣好像會讓 script 變得漏漏長一串,此時 set -e 指令就可以派上用場

set -e 這個指令會在任何指令失敗的時候,結束整個 script 的執行,意即:有任一指令出錯,就不會繼續執行下去

此指令等價於 set -o errexit

Without set-e

#!bin/bash

# 實際上不存在 a.txt 檔案,所以這邊會出錯
cp a.txt b.txt

# 結果上面出錯了,還是執行到這行
echo "如果上面有錯不要執行我"

Example without set-e

With set-e

#!bin/bash

# 加入這行,只要任一指令出錯即中斷
set -e

# 實際上不存在 a.txt 檔案,所以這邊會出錯
cp a.txt b.txt

# 上面出錯了,腳本中斷了,所以以下指令沒有被執行
echo "如果上面有錯不要執行我"

Exapmle With set-e

阿錯了直接離開,怎麼執行錯誤處理阿 💢

聰明的你可能會發現一些問題,如果還沒發現的朋友,可以執行下面的腳本看看!

#!bin/bash

set -e

# 實際上不存在 a.txt 檔案,所以這邊會出錯
cp a.txt b.txt
if [ $? -ne 0 ]
then
    echo "執行失敗"
fi

Exapmle error not handling

如果執行以上腳本,會出線上圖那個結果,看出來了嗎?

說好的錯誤處理呢???

理論上我們預期此腳本再出錯時,要印出「執行失敗」的文字,但因為我們設置了 set -e ,導致出錯後就直接結束了,後面的指令也就都執行不到了

有請 trap 指令登場 ⭐⭐⭐

trap 是 shell 內建的命令,通常用在腳本中指定信號的處理方式。trap 除了處理 Linux 信號外,還能對腳本退出(EXIT)調試(DEBUG)錯誤(ERR)返回(RETURN) 等以上情況指定處理方式。

簡單一句:trap 可以定義在離開腳本前要執行的指令

於是上面的那個腳本,就可以改成下方的版本:

#!bin/bash

set -e

# shell script 中的 function
ErrorHandling () {
    if [ $? -ne 0 ]
    then
        echo "執行失敗"
    fi
}

# 離開腳本前執行 ErrorHandling 這個 function
trap ErrorHandling EXIT

# 實際上不存在 a.txt 檔案,所以這邊會出錯
cp a.txt b.txt

Exapmle with trap

由以上結果可以確定,當腳本要離開前,有去執行 ErrorHandling function,在 function 裡面我們再去判斷最後一個指令的結果碼,然後再去做你要做的錯誤處理!