From 320691f312287575140bde777a11035166fc53a0 Mon Sep 17 00:00:00 2001 From: Einhard Leichtfuß Date: Sun, 22 Dec 2024 04:08:55 +0100 Subject: Initial commit The basic.bash script is based on the one used in `github.com:lawandorga/laworga-mail-server.git`, and other versions used by me (which the `lawandorga-mail-server.git` one was based upon). The notes are generally new, but many of them just a consolidation and refinement of existing knowledge (of mine). --- docs/error-handling.md | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 docs/error-handling.md (limited to 'docs/error-handling.md') diff --git a/docs/error-handling.md b/docs/error-handling.md new file mode 100644 index 0000000..483123f --- /dev/null +++ b/docs/error-handling.md @@ -0,0 +1,112 @@ +# 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 + +### 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](subshell.md) are not caught (properly): + * `declare var=$(false)` + * Instead, write: `declare var; var=$(false)` + * `true $(false)` + * Workaround: Use an intermediate variable. + * `true <(false)`, `true >(false)` + * Workaround: Use a pipe where possible. + * See also: [Pipes](#Pipes) + * Workaround: Use a temporary file. + * See also: [Backgrounded process](#Backgrounded_process) + + +### 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-)](subshell.md)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 also caught in + `declare var=$undef_var` (compare [Subshell section](#Subshell)). + * 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. + + +### 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](#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 + +* With [subshells](subshell.md), there may be + [multiple stack traces printed](error-handling/subshell/repeated-printing.md). -- cgit v1.2.3