Error handling
- A central feature of
basic.bash
is to exit (non-zero) on error and provide a useful error message, namely a stack trace.- In many cases, the failing command will additionally print an error message on its own, which should be printed above the stack trace.
- Manual error handling is still best, but often too time consuming when calling many external commands and bash builtins that may fail, as is common with shell scripts (unless they are very small).
Limitations
Introductory notes
- Much of the below is from empirical evidence (aka testing).
Backgrounded process
- An error in a backgrounded process (e.g.,
false &
) is ignored by the calling shell. - Failures within, e.g.,
{ false; } &
will cause a stack trace to be printed, but the calling shell will not terminate. - It may be a good idea to use
wait
to check on the return status. - It may further be a good idea to redefine the trap on
ERR
within a backgrounded process to not print a stack trace.
Subshell
- Some errors in subshells are not caught (properly):
declare var=$(false)
- Instead, write:
declare var; var=$(false)
- Instead, write:
true $(false)
- Workaround: Use an intermediate variable.
true <(false)
,true >(false)
- Workaround: Use a pipe where possible.
- See also: Pipes
- Workaround: Use a temporary file.
- See also: Backgrounded process
- Workaround: Use a pipe where possible.
Unset variables
- Unset arrays (when accessed with subscript
@
or*
) are never treated as an error.- To manually check for a non-empty array:
test -v var[@]
- To manually check for an unset array,
declare -p var
and/or${var@a}
may be helpful. - To manually check whether a variable is declared as an array,
declare -p var
and/or${var@a}
may be helpful.- The latter will fail (due to
set -o nounset
) if$var
is empty or unset. ${var[@]@a}
and${var[*]@a}
behave weirdly:- If
$var
is undeclared, evaluates to the empty array/string. - If
$var
is declared as an array, but unset, evaluates toa
. - If
$var
is set to the empty array, evaluates to the empty array/string. - If
$var
is set to a non-empty array, evaluates to the array (or space-separated list in case of${var[*]@a}
) of as manya
as the array is long.
- If
- The latter will fail (due to
- To manually check for a non-empty array:
- An error on an unset variable (due to
set -o nounset
) does not cause a stack trace to be printed in the current (sub-)shell, but only a simple message mentioning the file and line number.- If there is no subshell involved, no stack trace is printed at all.
- Note that unset variable errors are generally also caught in
declare var=$undef_var
andtrue $undef_var
.- Compare Subshell section.
- This does not hold if the shell is interactive (
bash -i
).
- This is arguably not a big problem; such errors should mostly be quickly
spotted by simple testing (or some static analysis tool).
- Exceptions: Usage of
declare -n
andeval
with dynamic variable names.
- Exceptions: Usage of
Array variables
- Attempting to assign
var[0]=(foo bar)
orvar['x']=(foo bar)
(where$var
is an array variable or associative array variable, respectively) causes an error to be printed instead of a stack trace in the innermost (sub-)shell.- Compare behaviour of unset variables.
- See also
src/basic.bash#errexit
.
- Attempting to assign
var['x']=foo
where$var
is not an associative array variable, has weird behaviour:- In any case, the (sub-)shell where the error originally occurs does not print a stacktrace, but a simple error message.
- If at least one subshell is involved, all but the lowest subshell print a stack trace and all (sub-)shells exit correctly.
- If no subshell is involved, the error message is printed, but execution is not aborted.
- Note that unlike for the preceding case,
set -o errexit
is irrelevant.
Pipes
- A pipeline is considered an error iff the last command returns non-zero.
- A different behaviour can be achieved by
set -o pipefail
.basic.bash
deliberately does not set this. See there for details.
- To catch errors in a non-last command of a pipeline, one should either
consult the
${PIPESTATUS[@]}
array variable, orset -o pipefail
in a subshell. - Oftentimes, avoiding pipes may be the best option.
Conditionals
- A command that is evaluated as a condition (e.g.,
if cmd; then ...; fi
), is never considered an error as the return code is instead used as a boolean condition. - In some cases it may be necessary for proper error reporting to do something
like the following:
if cmd ... else then if [[ $? -ne 1 ]] then return 1 fi ... fi
- See also: Pipes
Other use of stderr
- The
basic.bash
library assumes thatstderr
is never redirected, except directly from external commands or shell builtins (e.g.,var=$(cmd 2>&1)
).
Annoyances
- With subshells, there may be multiple stack traces printed.