使shopt改变本地功能

Tho*_*hor 20 bash shopt

我正在尝试编写一个使用bash函数nocasematch而不更改选项的调用者设置.功能定义是:

is_hello_world() {
  shopt -s nocasematch
  [[ "$1" =~ "hello world" ]] 
}
Run Code Online (Sandbox Code Playgroud)

在我打电话之前:

$ shopt nocasematch
nocasematch     off
Run Code Online (Sandbox Code Playgroud)

叫它:

$ is_hello_world 'hello world' && echo Yes
Yes
$ is_hello_world 'Hello World' && echo Yes
Yes
Run Code Online (Sandbox Code Playgroud)

正如所料,但现在nocasematch调用者已经改变:

$ shopt nocasematch
nocasematch     on
Run Code Online (Sandbox Code Playgroud)

有没有简单的方法使选项更改为函数本地?

我知道我可以检查返回值,shopt -q但这仍然意味着函数应该记住这一点并在退出之前重置它.

che*_*ner 31

函数体可以是任何复合命令,而不仅仅是组命令({}).使用子shell:

is_hello_world() (
  shopt -s nocasematch
  [[ "$1" =~ "hello world" ]] 
)
Run Code Online (Sandbox Code Playgroud)

  • `foo(){...}`看起来很自然,你从不认为大括号不是函数语法的一部分,而是最常用的复合命令. (14认同)
  • @davide产生新进程会有一些开销.但是,如果它在shell脚本中变得明显或有问题,则表明您应该使用其他语言.一个好的经验法则是任何shell脚本的运行时间应该由它运行的程序控制,而不是shell本身. (3认同)
  • 您可能更喜欢存储旧值,然后将其还原.子shelling效率低下,但在Bash的特殊情况下,在某些情况下,它会优化进程调用,重用相同的进程而不是分叉.`$ SHELLOPTS`和`$ BASHOPTS`是你的朋友. (2认同)
  • @chepner,确实...如果 `foo () { ( # ...; ); 会怎么样?}`? (2认同)

NoD*_*und 11

我知道这个帖子的日期是2012年,但您也可以执行以下操作(适用于Windows上的Git Bash 1.8.4,因此它适用于Linux):

function foobar() {
    local old=$(shopt -p extglob)
    shopt -s extglob

    ... your stuff here ...
    eval "$old"
}
Run Code Online (Sandbox Code Playgroud)

-p选项只需打印shopt -s extglob,如果extglob是,否则shopt -u extglob.

shopt -p 打印整个选项列表.

  • 就我而言,我并不在乎,我承认这可能和使用IFS一样糟糕。另一方面,我想像一个`pushd`这样的`pushshopt`函数很容易,并在设置它们之前使用bash数组记住先前的选项。像`pushshopt + extglob -nocasematch`和`popshopt`。 (2认同)
  • 请注意,如果使用`set -e`并将该语句分成`local old',old = $(shopt -p extglob)`,第二条语句将出错,因为`man bash(1)`指出:“如果启用所有optname,则列出选项时的返回状态为零,否则为非零”。在这种情况下,您需要编写类似`local old;之类的东西。old = $(shopt -p extglob || true)。使用一个语句时它起作用的原因是`local`吞噬了右侧的返回类型(例如`local foo = $(false)`实际上返回零状态代码);这是bash的众多陷阱之一。 (2认同)

Rob*_*ade 6

使用返回陷阱

在shell函数...返回之后,将在执行恢复之前执行用RETURN陷阱指定的命令。

-p[给禁用了javascript]选项将导致在可重用为输入的形式显示输出。

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

foobar() {
    trap "$(shopt -p extglob)" RETURN
    shopt -s extglob

    # ... your stuff here ...

}
Run Code Online (Sandbox Code Playgroud)

进行测试

foobar() {
    trap "$(shopt -p extglob)" RETURN
    shopt -s extglob

    echo "inside foobar"
    shopt extglob # Display current setting for errexit option
}

main() {
    echo "inside main"
    shopt extglob # Display current setting for errexit option
    foobar
    echo "back inside main"
    shopt extglob # Display current setting for errexit option
}
Run Code Online (Sandbox Code Playgroud)

测试

$ main
inside main
extglob         off
inside foobar
extglob         on
back inside main
extglob         off
Run Code Online (Sandbox Code Playgroud)

变体:要重置所有购物选项,请将trap语句更改为:

trap "$(shopt -p)" RETURN
Run Code Online (Sandbox Code Playgroud)

变体:要重置所有设置的选项,请将trap语句更改为:

trap "$(set +o)" RETURN
Run Code Online (Sandbox Code Playgroud)

注意:从Bash 4.4开始,有一个更好的方法:使$-local

变体:要重置所有set所有shopt选项,请将trap语句更改为:

trap "$(set +o)$(shopt -p)" RETURN
Run Code Online (Sandbox Code Playgroud)

注意: set +o等同于shopt -p -o

+ o 将当前选项设置以适合于作为实现相同选项设置的命令重新输入到Shell的格式写入标准输出。

Open Group基本规范,第7期,2018年版>集