Make shopt change local to function

I know this post date from 2012, but you can also do the following (works in Git Bash 1.8.4 on Windows, so it should work on Linux) :

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

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

The -p option simply print shopt -s extglob if extglob is on, otherwise shopt -u extglob.

shopt -p print the whole list of options.


You can use an associative array to remember the previous setting and then use it for reverting to the earlier setting, like this:

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
}

Then use these functions as:

... 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

or

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

Complete source code here: https://github.com/codeforester/base/blob/master/lib/shopt.sh


Use a RETURN trap

Commands specified with an RETURN trap are executed before execution resumes after a shell function ... returns...

The -p option [to shopt] causes output to be displayed in a form that may be reused as input.

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

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

    # ... your stuff here ...

}

For test

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
}

Test

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

Variation: To reset all shopt options, change the trap statement to:

trap "$(shopt -p)" RETURN

Variation: To reset all set options, change the trap statement to:

trap "$(set +o)" RETURN

Note: Starting with Bash 4.4, there's a better way: make $- local.

Variation: To reset all set and all shopt options, change the trap statement to:

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

NOTE: set +o is equivalent to shopt -p -o:

+o Write the current option settings to standard output in a format that is suitable for reinput to the shell as commands that achieve the same options settings.

– Open Group Base Specifications Issue 7, 2018 edition > set


The function body can be any compound command, not just a group command ( {} ). Use a sub-shell:

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

Tags:

Bash

Shopt