
#!/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
→ 由於重定向失敗而導致狀態 1false </dev/null
→ 狀態 1 來自false
a=b
→ 狀態 0,因為沒有任何可能發生故障的零件a=$(exit 0) b=$(exit 1) c=$(exit 2)
→ 上次指令替換的狀態 2a=$(exit 2) b=$(exit 1) c=$(exit 0)
→ 上次指令替換後的狀態 0true $(exit 0) $(exit 1) $(exit 2)
→ 狀態 0 來自true
再次強調,在所有情況下,set -e
只有當指令的狀態非零時才會停止腳本。嵌入命令的狀態不直接相關。
因此,在第二個腳本中,echo -n $(…)
狀態為 0,來自echo
(除非echo
無法寫入),無論命令替換中發生什麼。因此,即使腳本set -e
處於活動狀態,也不會在此停止。同樣,在第三個腳本中,f $(…)
的狀態為 0 f
。