Check for bash options
In bash-4.4
and above, you can use:
myfunction() {
local -
set -o someoption
...
}
A few notes:
- that's copied from
ash
. Inash
, the idea is to make$-
(which stores the set of options) local. It works inash
because inash
,local
doesn't affect the value nor attributes of the variable (contrary tobash
where it initially makes it unset). Still it also works withbash
the same way as inash
as a special case, that is it doesn't cause$-
to be unset or revert to a default behaviour - it only covers the options set with
set -o option
, not the ones set byshopt -s option
(bash
being the only shell with two sets of options!) like for local variables, it is dynamic scoping, not static. The value of
$-
will be restored upon returning from the function, but the new setting will affect the other functions or sourced scripts called by your function. If you want static scoping, you'll need to switch to ksh93 and use:function myfunction { set -o myoption ... other_function }
With the
other_function
not affected as long as it's declared with thefunction other_function {
syntax as well (and not the Bourneother_function()...
syntax).As it doesn't reset the options, your function may still be affected by options set by other code. To avoid that, you'd want to use
zsh
instead.zsh
's equivalent oflocal -
isset -o localoptions
, but you can also get to a well known emulation mode locally. For instance:myfunction() { emulate -L zsh set -o someoption ... }
would start with a sane zsh-default set of options (with some exceptions, see doc) on top of which you add your option. That's also dynamic scoping like in ash/bash, but you can also call other functions with:
emulate zsh -c 'other_function...'
for that
other_function
to be called with a vanilla zsh option set.zsh also has a number of operators that can make you avoid changing options globally in the first place (like
(N)
/(D)
glob qualifiers fornullglob
/dotglob
,(#i)
glob operator for case insensitive globbing,${(s:x:)var}
to avoid mingling with$IFS
,${~var}
to request globbing upon parameter expansion (you neednoglob
in other Bourne-like shells to avoid (!) it)...
The $SHELLOPTS
variable holds all set options separated by colons. As an example, it may look like:
braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
To check for a given option one may do the following:
# check if pipefail is set, if not set it and remember this
if [[ ! "$SHELLOPTS" =~ "pipefail" ]]; then
set -o pipefail;
PIPEFAIL=0;
else
PIPEFAIL=1;
fi
# do something here
# reset pipefail, if it was not set before
if [ "$PIPEFAIL" -eq 0 ]; then
set +o pipefail;
fi
If you want to set a shell option inside a function, but do not want to affect the caller, you do it two different ways:
Option 1: subshell
Put the function inside a subshell (notice the parenthesis surrounding the function's statements instead of curly brackets):
function foo() (
set -o pipefail
echo inside foo
shopt -o pipefail
# ...
)
Then, the caller can have pipefail unset and is not affected:
$ shopt -o pipefail
pipefail off
$ foo
inside foo
pipefail on
$ shopt -o pipefail
pipefail off
or
Option 2: test-and-reset
You could test-and-reset the option as needed (notice the curly braces around the function statements -- no subshell):
function foo() {
shopt -q -o pipefail
local resetpipefail=$?
set -o pipefail
echo inside foo
shopt -o pipefail
# ...
# only reset pipefail if needed
[[ $resetpipefail -eq 1 ]] && set +o pipefail
}
$ shopt -o pipefail
pipefail off
$ foo
inside foo
pipefail on
$ shopt -o pipefail
pipefail off
Hat tip to cuonglm for their shopt -q
answer.