繼承_errexit什麼時候不行?

繼承_errexit什麼時候不行?
#!/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

還有其他案例嗎?或一些概括?

答案1

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

相關內容