使用 RETURN 陷阱

使用 RETURN 陷阱

我有一個函數,想使用pipefail裡面的選項。但我不想簡單地這樣做set -o pipefail,因為我擔心腳本的另一部分可能不會pipefail被設定。當然我可以set +o pipefail稍後再做,但如果pipefail設定在我的函數之外,這也可能會引入意外的行為。

當然,如果設定了,我可以使用退出代碼false | true作為衡量標準pipefail,但這似乎有點髒。

是否有更通用(也許規範?)的方法來檢查設定的 bash 選項?

答案1

bash-4.4以上內容中,您可以使用:

myfunction() {
  local -
  set -o someoption
  ...
}

一些注意事項:

  • 這是從 複製的ash。在 中ash,想法是使$-(儲存選項集)本地化。它起作用是ash因為 in ash,local不會影響變數的值或屬性(與bash它最初取消設定的位置相反)。儘管如此,它也以與特殊情況bash相同的方式工作ash,也就是說,它不會導致$-被取消設定或恢復為預設行為
  • 它僅涵蓋使用 設定的選項set -o option,而不涵蓋使用 設定的選項shopt -s optionbash是唯一具有兩組選項的 shell!)
  • 與局部變數一樣,它是動態作用域,而不是靜態的。$-從函數返回時,的值將被恢復,但新設定將影響函數呼叫的其他函數或來源腳本。如果您想要靜態作用域,則需要切換到 ksh93 並使用:

    function myfunction {
      set -o myoption
      ...
      other_function
    }
    

    other_function只要它function other_function {也是用語法(而不是 Bourneother_function()...語法)聲明的,就不受影響。

  • 由於它不會重置選項,因此您的函數可能仍會受到其他程式碼設定的選項的影響。為了避免這種情況,您需要使用zsh它。zsh相當於local -is set -o localoptions,但你也可以得到一個眾所周知的模擬模式本地。例如:

    myfunction() {
      emulate -L zsh
      set -o someoption
      ...
    }
    

    將以一組合理的 zsh-default 選項開始(有一些例外,請參閱文件),在其上新增您的選項。這也是像 ash/bash 中那樣的動態作用域,但您也可以使用以下方式呼叫其他函數:

    emulate zsh -c 'other_function...'
    

    other_function以便使用普通 zsh 選項集來呼叫它。

    zsh 還有許多運算符,可讓您避免首先全域變更選項(例如(N)/用於/ 的glob(D)限定符,用於不區分大小寫的通配符的 glob 運算符,以避免與,混合)nullglobdotglob(#i)${(s:x:)var}$IFS${~var}要求在參數擴展時進行通配(您需要noglob在其他類似 Bourne 的 shell 中避免(!) 它)...

答案2

$SHELLOPTS變數保存所有用冒號分隔的設定選項。例如,它可能看起來像:

braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor

要檢查給定選項,可以執行以下操作:

# check if pipefail is set, if not set it and remember this
if [[ ! "$SHELLOPTS" =~ "pipefail" ]]; then
    set -o pipefail;
    PIPEFAIL=0;
else
    PIPEFAIL=1;
fi

# do something here

# reset pipefail, if it was not set before
if [ "$PIPEFAIL" -eq 0 ]; then
    set +o pipefail;
fi

答案3

如果您想在函數內設定 shell 選項,但又不想影響呼叫者,可以透過兩種不同的方式進行:

選項 1:子 shell

將函數放入子 shell 中(請注意函數語句周圍的括號而不是大括號):

function foo() (
  set -o pipefail
  echo inside foo
  shopt -o pipefail
  # ...
)

然後,呼叫者可以取消設定 pipelinefail 並且不受影響:

$ shopt -o pipefail
pipefail        off
$ foo
inside foo
pipefail        on
$ shopt -o pipefail
pipefail        off

或者

選項 2:測試和重置

您可以根據需要測試並重設該選項(請注意函數語句周圍的花括號—沒有子 shell):

function foo() {
  shopt -q -o pipefail
  local resetpipefail=$?
  set -o pipefail
  echo inside foo
  shopt -o pipefail
  # ...
  # only reset pipefail if needed
  [[ $resetpipefail -eq 1 ]] && set +o pipefail  
}

$ shopt -o pipefail
pipefail        off
$ foo
inside foo
pipefail        on
$ shopt -o pipefail
pipefail        off

帽子提示庫恩勒姆他們的shopt -q回答

答案4

使用 RETURN 陷阱

使用陷阱指定的命令在使用或傳回RETURN執行的 shell 函數或 shell 腳本還原之前執行。.source

[...]
選項-p[to shopt] 使輸出以可重複用作輸入的形式顯示。

https://www.gnu.org/software/bash/manual/bash.html

foo() {
  trap "$(shopt -p -o pipefail)" RETURN
  set -o pipefail
  echo inside foo
  shopt -o pipefail
  # ...
}

測試

$ shopt -o pipefail
pipefail        off
$ foo
inside foo
pipefail        on
$ shopt -o pipefail
pipefail        off

以下函數範本處理一般情況,確保函數在返回時setshopt選項都恢復為其原始值:

foo() {
  trap "$(shopt -p -o; shopt -p)" RETURN # ①
  # ...
}

shopt -p -o可簡化為標準等效set +o

相關內容