Bash

為什麼退出狀態非零的命令會發送 ERR 信號,即使它是“&& 或 || 的一部分”?列表”?

  • April 14, 2022

man bash包括使用此文件trap

trap [-lp] [[arg] sigspec ...]
   …
   The ERR trap is not executed if the failed command is part of the command list
   immediately following a while or until keyword, part of the test in an if statement,
   part of a && or || list, or if the command's return value is being inverted via !.
   These are the same conditions obeyed by the errexit option.
   …

我理解**“&& 或 || 的一部分 list”**表示如果命令是具有這些控制運算符的列表的一部分,即使它具有非零退出狀態,它也不應該發送 ERR 信號(或者如果使用 則退出腳本set -o errexit)。

然而,這是一個似乎與此相矛盾的測試腳本:

#!/usr/bin/env bash

_trap_err() {
   local status=$? sig=$1 line=$2;
   echo "Exit status ${status} on line ${line}: \`${BASH_COMMAND}\`";
}
trap '_trap_err ERR $LINENO' ERR;

function control_operators() {
   # The next line will send an ERR signal.
   [[ 1 -eq 2 ]] && echo Hello;
}

control_operators;

# The next line will not send an ERR signal.
[[ 1 -eq 2 ]] && echo Hello;

echo Done;

輸出是:

❯ test.sh
Exit status 1 on line 14: `[[ 1 -eq 2 ]]`
Done

因為[[ 1 -eq 2 ]]是“&& 或 || 的一部分” list”它不應該觸發 ERR 信號

預期的輸出是:

❯ test.sh
Done

此外,只有函式[[ 1 -eq 2 ]] && echo Hello 內部的列表control_operators發送 ERR 信號,而函式[[ 1 -eq 2 ]] && echo Hello 外部不發送。

此腳本環境中設置的選項是(從 輸出set -o | grep 'on$'):

braceexpand     on
hashall         on
interactive-comments    on
xtrace          on

問題:

  1. 這種行為是正常的,還是我在列表中錯誤地使用了控制運算符(或錯誤地解釋了文件)?
  2. 當命令是條件表達式的一部分並且實際上不構成錯誤狀態時,避免觸發 ERR 陷阱的最佳方法是什麼?
  • 一種選擇是 || true使用控制運算符添加到每個列表的末尾。
  • 另一種選擇是使用if [[ 1 -eq 2 ]]; then echo Hello; fi而不是&&速記;這不會導致發送 ERR 信號,因為(根據man bash)“if 語句中的測試的一部分”——儘管我很困惑為什麼當“&& 或 || 的一部分時它會起作用” 列表”沒有。
  1. 為什麼[[ 1 -eq 2 ]]只在函式內部發送 ERR 信號?

更新: @Kusalananda 的第一個答案對於上面的範例是正確的(添加return 0control_operators函式中可以防止 ERR 信號)。但是,這是另一個似乎違反“&& 或 || 的一部分”的範例。列表”規則:

#!/usr/bin/env bash
_trap_err() {
   local status=$? sig=$1 line=$2;
   echo "Exit status ${status} on line ${line}: \`${BASH_COMMAND}\`";
}
trap '_trap_err ERR $LINENO' ERR;
true && false;
echo Done;

這個腳本的輸出是:

❯ test.sh
Exit status 1 on line 7: `false`
Done

該行true && false不應發送 ERR 信號,因為該命令false是“&& 或 || 的一部分”。列表”。那麼,為什麼這樣做呢?

**更新 2:我正在使用 bash 3.2 在 macOS 上工作,這就是為什麼上面的程式碼片段man bash沒有提到“除了最後的 && 或 || 之後的命令” . 😵‍💫 再次感謝@Kusalananda 提供缺失的細節。

ERR由於control_operators呼叫的返回狀態非零,您的腳本正在觸發陷阱。函式中的測試不會直接觸發陷阱。

陷阱輸出通過它列印的行號來指示這一點,這將是呼叫函式的行。

陷阱輸出還指示此非零退出狀態來自何處,這是函式中測試的結果。由於測試是函式中執行的最後一件事,因此這將設置函式的退出狀態。

函式呼叫不是 AND 或 OR 列表的一部分,因此會觸發陷阱。


對於您更新的問題,您忘記瞭ERR如果返回非零退出狀態的命令是 AND 或 OR 列表中的最後一個命令,則仍然會呼叫陷阱。

bash手冊中,我強調:

如果失敗的ERR命令是緊跟在whileoruntil關鍵字之後的命令列表的一部分、if語句中測試的一部分、在&&or||列表中執行的命令的一部分(**除了最後一個&&or||**之後的命令、a 中的任何命令),則不會執行陷阱管道但最後一個,或者如果命令的返回值正在使用 !.

您的範例是true && false,並且由於false是 AND 列表中的最後一個,因此它觸發了ERR陷阱。

另一種說法是,如果決定列表或管道退出狀態的命令返回非零退出狀態(如果與該命令一起使用,則返回零退出狀態),ERR則呼叫陷阱。!

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