Bash

條件中發生錯誤時,“set -e”不會終止腳本

  • November 29, 2017

以下腳本存在語法錯誤或某種錯誤:

#!/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_COMMANDwas [ -f / ]

無論如何,像

[ -f /] | cat
export var="$([ -f /])"

無法檢測到退出狀態,因為退出狀態未傳播到父 shell 程序(pipefail第一種情況下的選項除外)。

現在,我不確定在執行時添加這種(脆弱的)檢測是否值得,因為在開發時很容易檢測到錯誤(當您編寫和測試腳本時)。

引用自:https://unix.stackexchange.com/questions/407716