Commit | Line | Data |
---|---|---|
78a1a75c | 1 | #!/bin/bash |
997eb0be IK |
2 | # Copyright 2018 Ian Kelling |
3 | ||
4 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | # you may not use this file except in compliance with the License. | |
6 | # You may obtain a copy of the License at | |
7 | ||
8 | # http://www.apache.org/licenses/LICENSE-2.0 | |
9 | ||
10 | # Unless required by applicable law or agreed to in writing, software | |
11 | # distributed under the License is distributed on an "AS IS" BASIS, | |
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | # See the License for the specific language governing permissions and | |
14 | # limitations under the License. | |
15 | ||
acd03e1e | 16 | |
acd03e1e IK |
17 | # Commentary: Bash stack trace and error handling functions. This file |
18 | # is meant to be sourced. It loads some functions which you may want to | |
19 | # call manually (see the comments at the start of each one), and then | |
20 | # runs err-catch. See the README file for a slightly longer explanation. | |
21 | ||
a5c2ac43 | 22 | |
a5c2ac43 IK |
23 | ####################################### |
24 | # Print stack trace | |
25 | # | |
26 | # usage: err-bash-trace [FRAME_START] | |
27 | # | |
62aed8de IK |
28 | # This function is called by the other functions which print stack |
29 | # traces. | |
30 | # | |
a5c2ac43 IK |
31 | # It does not show function args unless you first run: |
32 | # shopt -s extdebug | |
62aed8de | 33 | # which err-catch & err-print do for you. |
a5c2ac43 IK |
34 | # |
35 | # FRAME_START The frame to start printing on. default=0. Useful when | |
36 | # printing from an ERR trap function to avoid printing | |
37 | # that function. | |
38 | ####################################### | |
39 | err-bash-trace() { | |
40 | local -i argc_index=0 frame i start=${1:-0} max_indent=8 indent | |
acd03e1e IK |
41 | local source |
42 | local extdebug=false | |
43 | if [[ $(shopt -p extdebug) == *-s* ]]; then | |
44 | extdebug=true | |
45 | fi | |
46 | for ((frame=0; frame < ${#FUNCNAME[@]}-1; frame++)); do | |
47 | argc=${BASH_ARGC[frame]} | |
48 | argc_index+=$argc | |
fb79b8bd | 49 | ((frame < start)) && continue |
acd03e1e IK |
50 | if (( ${#BASH_SOURCE[@]} > 1 )); then |
51 | source="${BASH_SOURCE[frame+1]}:${BASH_LINENO[frame]}:" | |
52 | fi | |
fb79b8bd | 53 | indent=$((frame-start + 1)) |
acd03e1e IK |
54 | indent=$((indent < max_indent ? indent : max_indent)) |
55 | printf "%${indent}s↳%sin \`%s" '' "$source" "${FUNCNAME[frame]}" | |
56 | if $extdebug; then | |
57 | for ((i=argc_index-1; i >= argc_index-argc; i--)); do | |
58 | printf " %s" "${BASH_ARGV[i]}" | |
59 | done | |
78a1a75c | 60 | fi |
acd03e1e IK |
61 | echo \' |
62 | done | |
a5c2ac43 | 63 | return 0 |
78a1a75c | 64 | } |
364203b2 | 65 | |
a5c2ac43 IK |
66 | ####################################### |
67 | # On error print stack trace and exit | |
68 | # | |
69 | # Globals: | |
70 | # ${_errcatch_cleanup[@]} Optional command & args that will run before exiting | |
71 | ####################################### | |
997eb0be | 72 | err-catch() { |
acd03e1e IK |
73 | set -E; shopt -s extdebug |
74 | _err-trap() { | |
75 | err=$? | |
76 | exec >&2 | |
77 | set +x | |
9ce3dca1 IK |
78 | echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err" |
79 | err-bash-trace 2 | |
80 | set -e # err trap does not work within an error trap | |
a0551468 | 81 | "${_errcatch_cleanup[@]:-:}" # note :-: is to be compatible with set -u |
acd03e1e IK |
82 | echo "$0: exiting with code $err" |
83 | exit $err | |
84 | } | |
85 | trap _err-trap ERR | |
86 | set -o pipefail | |
78a1a75c | 87 | } |
364203b2 | 88 | |
62aed8de IK |
89 | ####################################### |
90 | # Undoes err-catch. turns off exit and stack trace on error. | |
91 | ####################################### | |
92 | err-allow() { | |
93 | set +E +o pipefail; trap ERR | |
94 | } | |
95 | ||
a5c2ac43 IK |
96 | ####################################### |
97 | # On error, print stack trace | |
98 | ####################################### | |
9ce3dca1 IK |
99 | err-print() { |
100 | # help: on errors: print stack trace | |
101 | # | |
102 | # This function depends on err-bash-trace. | |
103 | ||
104 | set -E; shopt -s extdebug | |
105 | _err-trap() { | |
106 | err=$? | |
107 | exec >&2 | |
108 | set +x | |
109 | echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}: \`$BASH_COMMAND' returned $err" | |
110 | err-bash-trace 2 | |
111 | } | |
112 | trap _err-trap ERR | |
113 | set -o pipefail | |
114 | } | |
115 | ||
116 | ||
a5c2ac43 | 117 | ####################################### |
62aed8de | 118 | # Print stack trace and exit |
a5c2ac43 IK |
119 | # |
120 | # Use this instead of the exit command to be more informative. | |
121 | # | |
122 | # usage: err-exit [EXIT_CODE] [MESSAGE] | |
123 | # | |
124 | # EXIT_CODE Default is 1. | |
125 | # MESSAGE Print MESSAGE to stderr. If only one of EXIT_CODE | |
126 | # and MESSAGE is given, we consider it to be an | |
127 | # exit code if it is a number. | |
128 | ####################################### | |
997eb0be | 129 | err-exit() { |
acd03e1e IK |
130 | exec >&2 |
131 | code=1 | |
41be570a | 132 | if [[ "$*" ]]; then |
acd03e1e IK |
133 | if [[ ${1/[^0-9]/} == "$1" ]]; then |
134 | code=$1 | |
135 | if [[ $2 ]]; then | |
a5c2ac43 | 136 | printf '%s\n' "$2" >&2 |
acd03e1e IK |
137 | fi |
138 | else | |
a5c2ac43 | 139 | printf '%s\n' "$0: $1" >&2 |
78a1a75c | 140 | fi |
acd03e1e IK |
141 | fi |
142 | echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}" | |
143 | err-bash-trace 2 | |
144 | echo "$0: exiting with code $code" | |
145 | exit $err | |
78a1a75c | 146 | } |
364203b2 | 147 | |
a5c2ac43 | 148 | # We want this more often than not, so run it now. |
acd03e1e | 149 | err-catch |