條件中發生錯誤時,“set -e”不會終止腳本
以下腳本存在語法錯誤或某種錯誤:
#!/usr/bin/env bash set -euo pipefail if [ ! -f /custom.log]; then echo "test" fi abcxyz
腳本失敗,輸出如下:
./test.sh: line 4: [: missing `]' ./test.sh: line 7: abcxyz: command not found
我不關心如何修復此腳本,但如果遇到此錯誤,如何防止腳本繼續執行?我原以為
set -e
會強制執行這種行為。
set -e
不會觸發失敗的命令,這些命令用作條件部分中的條件,例如if
/while
/until
構造的條件部分或 a 的左側||
,或在這些條件下呼叫&&
的函式、子shell、源文件、 ed 程式碼中。eval
如果是這樣,那麼:
if [ ! -f /custom.log ]; then
如果
/custom.log
是正常文件,[
將退出腳本,然後也會以非零退出狀態退出。如果不滿足測試條件並且存在語法錯誤(但並非所有語法錯誤,例如,不是 in ),則 shell的
[
內置命令bash
(以及大多數其他實現)將以狀態退出。POSIX 要求退出狀態大於 1 以防出錯。1``2``[ -v 'a[+]' ]
因此,如果命令以大於 1 的程式碼退出,您可以選擇退出腳本,無論它是否在條件中使用,例如:
shopt -s extdebug # make sure the DEBUG trap propagates to subshells trap '(($?>1 && (ret=$?))) && exit "$ret"' DEBUG [ -f / ] || echo / not a regular file # OK [ -f /] || echo was a syntax error # causes an exit, not output echo not reached
請注意,您不能為此使用
ERR
陷阱,因為ERR
陷阱僅在與觸發退出的條件相同的條件下執行set -e
。現在,請注意其影響。例如,這將導致:
if grep -qs pattern /file; then echo pattern was found in /file fi
/file
如果不存在或不可讀,則退出,因為grep
在這種情況下返回 2 狀態,即使使用-s
,其意圖顯然是忽略這些情況。因此,您需要注意在哪些條件下您在條件中使用的命令可能會以大於 1 的狀態退出。要解決這些問題,您需要以下內容:
if sh -c 'grep -sq pattern / file || exit 1'; then...
您可以使用以下命令將退出狀態大於 1 時的退出限制為
[
ortest
命令:unset -v previous_BASH_COMMAND trap ' case $previous_BASH_COMMAND in ("[ "* | "test "*) (($?>1 && (ret=$?))) && exit "$ret" esac previous_BASH_COMMAND=$BASH_COMMAND' DEBUG
這有一些限制。在
echo x ([ -f/]; echo y)
這將導致子外殼退出,但不會導致父外殼退出,因為
$previous_BASH_COMMAND
尚未在那裡設置。在:[ -f / ] && echo a regular file (grep -qs foo /file && echo foo in /file) echo here
shell 將在執行時退出
echo here
,因為$?
會是 2 和$previous_BASH_COMMAND
was[ -f / ]
。無論如何,像
[ -f /] | cat export var="$([ -f /])"
無法檢測到退出狀態,因為退出狀態未傳播到父 shell 程序(
pipefail
第一種情況下的選項除外)。現在,我不確定在執行時添加這種(脆弱的)檢測是否值得,因為在開發時很容易檢測到錯誤(當您編寫和測試腳本時)。