summaryrefslogtreecommitdiff
path: root/docs/error-handling.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/error-handling.md')
-rw-r--r--docs/error-handling.md112
1 files changed, 112 insertions, 0 deletions
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).