aboutsummaryrefslogtreecommitdiff

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)
    • true $(false)
      • Workaround: Use an intermediate variable.
    • true <(false), true >(false)

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 to a.
        • 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 many a as the array is long.
  • 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 and true $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 and eval with dynamic variable names.

Array variables

  • Attempting to assign var[0]=(foo bar) or var['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.
  • 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, or set -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 that stderr is never redirected, except directly from external commands or shell builtins (e.g., var=$(cmd 2>&1)).

Annoyances