Bash

什麼時候inherit_errexit 不起作用?

  • October 20, 2021
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
a=$(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
f() { :; }
f $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived

還有其他情況嗎?還是某種概括?

TL,DR:為了受益set -e,直接將命令替換的結果分配給變數(可選地在其周圍加上額外的字元串)。不要將多個命令替換組合在一起或在命令參數中使用命令替換。

問題不在於inherit_errexit. 它正在工作。問題是set -e(不是特定於 bash 的限制:其他類似 sh 的 shell 也有同樣的問題)。

展示:執行第二個範例的這個變體。

$ cat b2.sh 
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file; echo >&2 after cat)
echo survived
$ ./b2.sh 
cat: no-such-file: No such file or directory
survived

請注意,echo >&2 after cat未執行。如果inherit_errexit是關閉的,那將是。

問題是set -e僅在簡單情況下停止執行錯誤。如果命令替換返回失敗狀態,則不會停止執行包含替換的簡單命令。它最多可能會設置簡單命令的返回狀態,這反過來可能會停止腳本的執行。“簡單命令”由賦值、重定向和可選的可執行命令名稱和參數組成。如果重定向失敗,則返回狀態為1。否則,如果有命令名,則簡單命令的返回狀態為可執行命令的返回狀態。否則返回狀態是最後一個命令替換的返回狀態,如果沒有,則返回 0。以下是一些簡單命令的範例:

  • true </no/such/file→ 狀態 1 由於重定向失敗
  • false </dev/null→ 狀態 1 從false
  • a=b→ 狀態 0,因為沒有任何可能失敗的部分
  • a=$(exit 0) b=$(exit 1) c=$(exit 2)→ 上次命令替換的狀態 2
  • a=$(exit 2) b=$(exit 1) c=$(exit 0)→ 上次命令替換的狀態 0
  • true $(exit 0) $(exit 1) $(exit 2)→ 狀態 0 從true

再一次,在所有情況下,set -e僅當命令的狀態為非零時才會停止腳本。嵌入式命令的狀態不直接相關。

因此,在您的第二個腳本echo -n $(…)中,狀態為 0,來自echo(除非echo無法寫入),無論命令替換內部發生什麼。因此,即使set -e處於活動狀態,腳本也不會在這裡停止。同樣,在第三個腳本f $(…)中,狀態為 0 來自f.

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