為什麼退出狀態非零的命令會發送 ERR 信號,即使它是“&& 或 || 的一部分”?列表”?
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
問題:
- 這種行為是正常的,還是我在列表中錯誤地使用了控制運算符(或錯誤地解釋了文件)?
- 當命令是條件表達式的一部分並且實際上不構成錯誤狀態時,避免觸發 ERR 陷阱的最佳方法是什麼?
- 一種選擇是
|| true
使用控制運算符添加到每個列表的末尾。- 另一種選擇是使用
if [[ 1 -eq 2 ]]; then echo Hello; fi
而不是&&
速記;這不會導致發送 ERR 信號,因為(根據man bash
)“if 語句中的測試的一部分”——儘管我很困惑為什麼當“&& 或 || 的一部分時它會起作用” 列表”沒有。
- 為什麼
[[ 1 -eq 2 ]]
只在函式內部發送 ERR 信號?更新: @Kusalananda 的第一個答案對於上面的範例是正確的(添加
return 0
到control_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
命令是緊跟在while
oruntil
關鍵字之後的命令列表的一部分、if
語句中測試的一部分、在&&
or||
列表中執行的命令的一部分(**除了最後一個&&
or||
**之後的命令、a 中的任何命令),則不會執行陷阱管道但最後一個,或者如果命令的返回值正在使用!
.您的範例是
true && false
,並且由於false
是 AND 列表中的最後一個,因此它觸發了ERR
陷阱。另一種說法是,如果決定列表或管道退出狀態的命令返回非零退出狀態(如果與該命令一起使用,則返回零退出狀態),
ERR
則呼叫陷阱。!