30

我正在尝试编写一个 bash 函数,该函数在nocasematch不更改选项的调用者设置的情况下使用。函数定义为:

is_hello_world() {
  shopt -s nocasematch
  [[ "$1" =~ "hello world" ]] 
}

在我调用它之前:

$ shopt nocasematch
nocasematch     off

叫它:

$ is_hello_world 'hello world' && echo Yes
Yes
$ is_hello_world 'Hello World' && echo Yes
Yes

正如预期的那样,但现在nocasematch调用者已经改变:

$ shopt nocasematch
nocasematch     on

是否有任何简单的方法可以使选项更改为函数的本地?

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

4

4 回答 4

44

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

is_hello_world() (
  shopt -s nocasematch
  [[ "$1" =~ "hello world" ]] 
)
于 2012-08-29T14:00:15.463 回答
21

使用 RETURN 陷阱

使用 RETURN 陷阱指定的命令在 shell 函数...返回后继续执行之前执行。

-p选项 [to shopt] 导致输出以可重复用作输入的形式显示。

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

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

    # ... your stuff here ...

}

用于测试

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
}

测试

$ main
inside main
extglob         off
inside foobar
extglob         on
back inside main
extglob         off

变化:要重置所有 shopt选项,请将陷阱语句更改为:

trap "$(shopt -p)" RETURN

变化:要重置所有设置选项,请将陷阱语句更改为:

trap "$(set +o)" RETURN

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

变化:要重置所有设置所有 shopt选项,请将陷阱语句更改为:

trap "$(set +o); $(shopt -p)" RETURN

注意: set +o相当于shopt -p -o

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

开放组基本规范第 7 期,2018 年版 > 设置

于 2018-06-12T02:35:20.773 回答
13

我知道这篇文章的日期是 2012 年,但您也可以执行以下操作(在 Windows 上的 Git Bash 1.8.4 中工作,因此它应该在 Linux 上工作):

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

    ... your stuff here ...
    eval "$old"
}

-p选项仅打印shopt -s extglobifextglob为 on,否则为shopt -u extglob

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

于 2013-11-27T23:27:18.387 回答
2

您可以使用关联数组来记住之前的设置,然后使用它来恢复到之前的设置,如下所示:

shopt_set

declare -gA _shopt_restore
shopt_set() {
    local opt count
    for opt; do
        if ! shopt -q "$opt"; then
            echo "$opt not set, setting it"
            shopt -s "$opt"
            _shopt_restore[$opt]=1
            ((count++))
        else
            echo "$opt set already"
        fi
    done
}

shopt_unset

shopt_unset() {
    local opt restore_type
    for opt; do
        restore_type=${_shopt_restore[$opt]}
        if shopt -q "$opt"; then
            echo "$opt set, unsetting it"
            shopt -u "$opt"
            _shopt_restore[$opt]=2
        else
            echo "$opt unset already"
        fi
        if [[ $restore_type == 1 ]]; then
            unset _shopt_restore[$opt]
        fi
    done
}

shopt_restore

shopt_restore() {
    local opt opts restore_type
    if (($# > 0)); then
        opts=("$@")
    else
        opts=("${!_shopt_restore[@]}")
    fi
    for opt in "${opts[@]}"; do
        restore_type=${_shopt_restore[$opt]}
        case $restore_type in
        1)
            echo "unsetting $opt"
            shopt -u "$opt"
            unset _shopt_restore[$opt]
            ;;
        2)
            echo "setting $opt"
            shopt -s "$opt"
            unset _shopt_restore[$opt]
            ;;
        *)
            echo "$opt wasn't changed earlier"
            ;;
        esac
    done
}

然后将这些函数用作:

... some logic ...
shopt_set nullglob globstar      # set one or more shopt options
... logic that depends on the above shopt settings
shopt_restore nullglob globstar  # we are done, revert back to earlier setting

或者

... some logic ...
shopt_set nullglob
... some more logic ...
shopt_set globstar
... some more logic involving shopt_set and shopt_unset ...
shopt_restore             # restore everything

完整的源代码在这里:https ://github.com/codeforester/base/blob/master/lib/shopt.sh

于 2019-06-28T14:57:44.753 回答