使用 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

相关内容