2 # Copyright (C) 2019 Ian Kelling
3 # SPDX-License-Identifier: GPL-3.0-or-later
5 # Commentary: Bash stack trace and error handling functions. This file
6 # is meant to be sourced. It loads some functions which you may want to
7 # call manually (see the comments at the start of each one), and then
8 # runs err-catch. See the README file for a slightly longer explanation.
11 #######################################
14 # usage: err-bash-trace [MESSAGE]
16 # This function is called by the other functions which print stack
19 # It does not show function args unless you first run:
21 # which err-catch & err-print do for you.
23 # MESSAGE Message to print just before the stack trace.
25 # _frame_start Optional variable to set before calling. The frame to
26 # start printing on. default=1. Useful when printing from
27 # an ERR trap function to avoid printing that function.
28 #######################################
30 local -i argc_index
=0 frame i start
=${_frame_start:-1}
35 for ((frame
=0; frame
< ${#FUNCNAME[@]}; frame
++)); do
36 argc
=${BASH_ARGC[frame]}
38 ((frame
< start
)) && continue
39 if (( ${#BASH_SOURCE[@]} > 1 )); then
40 source="${BASH_SOURCE[frame]}:${BASH_LINENO[frame-1]}:"
42 printf " from %sin \`%s" "$source" "${FUNCNAME[frame]}"
43 if shopt extdebug
>/dev
/null
; then
44 for ((i
=argc_index-1
; i
>= argc_index-argc
; i--
)); do
45 printf " %s" "${BASH_ARGV[i]}"
53 #######################################
54 # On error print stack trace and exit
57 # errcatch-cleanup If set, this command will run just before exiting.
58 #######################################
61 # This condition avoids starting the bash debugger in the case that
62 # this is sourced from a startup file, and you use a login shell to
63 # run a command. Avoid doing that if you want function arguments in
65 if [[ $
- != *c
* ]] ||
! shopt login_shell
>/dev
/null
; then
72 local msg
="${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err"
73 if (( ${#FUNCNAME[@]} > 2 )); then
79 set -e # err trap does not work within an error trap
80 if type -t errcatch-cleanup
>/dev
/null
; then
83 echo "$0: exiting with status $err"
91 #######################################
92 # Internal function for err-catch-interactive.
93 # Prints stack trace from interactive shell trap.
94 # Usage: see err-catch-interactive
95 #######################################
97 _err-bash-trace-interactive
() {
98 local ret bash_command argc pattern i
99 # We have these passed to us because they are lost inside the
106 for pattern
in "${err_catch_ignore[@]}"; do
107 # shellcheck disable=SC2053
108 if [[ ${BASH_SOURCE[0]} == $pattern ]]; then
112 if (( ${#FUNCNAME[@]} > _err_func_last
)); then
113 echo ERR
: \
`$bash_command\' returned $ret
115 _err_func_last=${#FUNCNAME[@]}
116 if (( _err_func_last > 1 )); then
117 printf " from \`%s
" "${FUNCNAME[1]}"
118 if shopt extdebug >/dev/null; then
119 for ((i=argc; i >= 0; i--)); do
120 printf " %s
" "${argv[i]}"
123 printf "\' defined
at %s
:%s
\n" "${BASH_SOURCE[1]}" "$(declare -F "${FUNCNAME[1]}"|awk "{print \$2}")"
128 #######################################
129 # For interactive shells: on error, print stack trace and return
131 # Note: calling line number is not available, so we print function
135 # err_catch_ignore Array containing glob patterns to test against filenames to ignore
136 # errors from. Initialized to ignore bash-completion scripts on debian
138 # _err_func_last Used internally in err-bash-trace-interactive
140 #######################################
141 err-catch-interactive
() {
142 if ! test ${err_catch_ignore+defined}; then
144 '/etc/bash_completion.d/*'
145 '*/bash-completion/*'
148 declare -i _err_func_last
=0
149 set -E; shopt -s extdebug
150 # shellcheck disable=SC2154
151 trap '_err-bash-trace-interactive $? "$BASH_COMMAND" ${BASH_ARGC[0]} "${BASH_ARGV[@]}"' ERR
156 #######################################
157 # Undoes err-catch/err-catch-interactive
158 #######################################
165 #######################################
166 # On error, print stack trace
167 #######################################
169 # help: on errors: print stack trace
171 # This function depends on err-bash-trace.
173 set -E; shopt -s extdebug
178 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err"
186 #######################################
187 # Print stack trace and exit
189 # Use this instead of the exit command to be more informative.
191 # usage: err-exit [EXIT_CODE] [MESSAGE]
193 # EXIT_CODE Default is 1.
194 # MESSAGE Print MESSAGE to stderr. If only one of EXIT_CODE
195 # and MESSAGE is given, we consider it to be an
196 # exit code if it is a number.
197 #######################################
202 if [[ ${1/[^0-9]/} == "$1" ]]; then
205 printf '%s\n' "$2" >&2
208 printf '%s\n' "$0: $1" >&2
211 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}"
213 echo "$0: exiting with code $code"
217 # We want this more often than not, so run it now.
218 if [[ $
- == *i
* ]]; then
219 err-catch-interactive