當條件發生錯誤時,「set -e」不會終止腳本

當條件發生錯誤時,「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會強制執行這種行為。

答案1

set -e不會在用作條件的命令失敗時觸發,例如//if建構的條件部分或 a 的左側,或在這些條件下呼叫的函數、子 shell、原始檔、ed 程式碼中。whileuntil||&&eval

如果確實如此,那麼:

if [ ! -f /custom.log ]; then

/custom.log如果是常規文件,則將退出腳本,[然後也會以非零退出狀態退出。

如果不滿足測試條件,並且存在語法錯誤(但並非所有語法錯誤,例如,不在 中),則shell[的內建命令(bash以及大多數其他實作)將以狀態退出。12[ -v 'a[+]' ]POSIX 要求退出狀態大於 1,以防出現錯誤

因此,如果命令以大於 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)

這將導致子 shell 退出,但不會導致父 shell 退出,因為$previous_BASH_COMMAND尚未在那裡設定。並在:

[ -f / ] && echo a regular file
(grep -qs foo /file && echo foo in /file)
echo here

shell 將在運行時退出echo here,因為$? will 是 2 且$previous_BASH_COMMANDwas [ -f / ]

無論如何,像這樣的事情

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

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

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

相關內容