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" ]]
)