Сделать локальное изменение магазина

Я пытаюсь написать функцию 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 но это все равно означает, что функция должна запомнить это и сбросить его перед выходом.

Ответ 1

Тело функции может быть любой составной командой, а не только командой группы ({}). Используйте под-оболочку:

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

Ответ 2

Я знаю эту дату публикации с 2012 года, но вы также можете сделать следующее (работает в Git Bash 1.8.4 на Windows, поэтому он должен работать на Linux):

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

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

Опция -p просто печатает shopt -s extglob если extglob, иначе shopt -u extglob.

shopt -p распечатает весь список опций.

Ответ 3

Используйте ловушку RETURN

Команды, заданные с помощью ловушки RETURN, выполняются до того, как выполнение возобновится после того, как функция оболочки... возвращает...

Параметр -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

Вариация: Чтобы сбросить все параметры магазина, измените оператор ловушки на:

trap "$(shopt -p)" RETURN

Вариация: Чтобы сбросить все параметры набора, измените оператор ловушки на:

trap "$(set +o)" RETURN

Примечание. Начиная с Bash 4.4, есть лучший способ: make $ - local.

Вариация: Чтобы сбросить все настройки и все параметры магазина, измените оператор ловушки на:

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

ПРИМЕЧАНИЕ: set +o эквивалентен shopt -p -o:

+o Запишите текущие настройки параметров на стандартный вывод в формате, подходящем для повторного включения в оболочку, в качестве команд, которые выполняют те же настройки параметров.

- Открытые базовые спецификации группы Издание 7, 2018 издание> установлено

Ответ 4

Вы можете использовать ассоциативный массив, чтобы запомнить предыдущий параметр, а затем использовать его для возврата к предыдущему параметру, например так:

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=("[email protected]")
    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