Three methods to consider:
- Using
shopt -po errexit to save the code needed to restore it in a
variable and calling it later.
- Using
shopt -qo errexit which tells you on or off via the exit code
- Using a subshell inside your function which is impossible to get
wrong but also is not always applicable depending on what your
function does.
Save and restore
If you want to set it back to what it was, there is an easy way to do this:
my_func(){
local restore_errexit="$(shopt -po errexit)"
set +e
# do stuff
${restore_errexit}
}
This is because (man bash) -p tells it to output the code necessary to set the option to whatever it is right now.
shopt [-pqsu] [-o] [optname ...]
[...] The -p option causes output to be displayed in a form that
may be reused as input
-q Suppresses normal output (quiet mode); the return status
indicates whether the optname is set or unset. [...]
-o Restricts the values of optname to be those defined
for the -o option to the set builtin.
EDIT: Note -o in the manpage. If an options is one of the options that can be set or unset with set builtin, then the -o is needed. So shopt -s extdebug, shopt -so xtrace
Control if statements
You may still want to do something else based on whether it was on or off, you can use [[ -o errexit ]] as was suggested in other answers but using shopt -q can work with both set and non-set options like extdebug (just don't put `-o'):
my_func(){
local errexit_was_on
if shopt -qo errexit ; then
echo "${FUNCNAME[0]}: INFO: turning off errexit for this function"
set +o errexit # same as `set +e` or `shopt -uo errexit`
errexit_was_on=true
else
errexit_was_on=false
fi
do_stuff
if [[ ${errexit_was_on} == true ]] ; then
echo "${FUNCNAME[0]}: INFO: reactivating errexit for this function"
set -o errexit # same as `set +e` or `shopt -so errexit`
fi
}
If your only goal is to save it and restore it to what it was and you don't care what it was, then I would use the first method and only use this one if you actually want to do one thing or the other based on the value of the option.
I think that @mmoossen's answer is a bit more clear than this but it only works for set options and not for shopt options.
Subshells
Another alternative is to use subshells. That depends on what your function does. If your function produces output and returns an exit code you can do it but if it has side effects then it depends: if the side effect is creating files, no problem, if it's setting outer-scope variables, it won't work.
my_func(){
(
set -e
do_stuff
)
}
Since the set -e happens in a subshell, it can't affect the main process
I usually start with this because you can't get it wrong. With that, I try to communicate by producing output. If that turns out to be inconvenient to the point where it becomes worth it to communicate by setting outer-scope variables, then I pivot to the first method. Finally if I want to print some messages, then I use the second method.
Side note: Bash allows you to do
my_func()(
)
which is a shorthand for the same thing above so that your entire function
happens in a subshell. I would advise against doing that: I used to do it but once I started modifying one such function and had forgotten that it was a subshell. One of those modifications was having it set the value of a variable from the outer scope. I started to believe that my computer was haunted because I was setting that variable and the calling code couldn't see the new value. I spent more time than I'd like to admit trying to debug this.