|
| 1 | +#!/bin/bash |
| 2 | +# |
| 3 | +# a common lib to show trapped error info. |
| 4 | +# |
| 5 | +# provide function `trap_error_info::register_show_error_info_handler` to register |
| 6 | +# the error-trap handler which show error info when trapped error. |
| 7 | +# |
| 8 | +# by default, auto register_show_error_info_handler when source this script; |
| 9 | +# disable by define `TRAP_ERROR_NO_AUTO_REGISTER` var |
| 10 | +# |
| 11 | +#_ source guard start _# |
| 12 | +[ -z "${__source_guard_84949D19_1C7A_40AF_BC28_BA5967A0B6CE:+dummy}" ] || return 0 |
| 13 | +__source_guard_84949D19_1C7A_40AF_BC28_BA5967A0B6CE="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" |
| 14 | +readonly __source_guard_84949D19_1C7A_40AF_BC28_BA5967A0B6CE |
| 15 | +#_ source guard end _# |
| 16 | + |
| 17 | +set -eEu -o pipefail -o functrace |
| 18 | + |
| 19 | +################################################################################ |
| 20 | +# api functions: |
| 21 | +# - trap_error_info::register_show_error_info_handler |
| 22 | +# - trap_error_info::show_stack_trace |
| 23 | +################################################################################ |
| 24 | + |
| 25 | +# trap_error_info::_get_caller_line_no $level |
| 26 | +# level = 0 means caller stack |
| 27 | +# |
| 28 | +# do NOT call this function in sub-shell! |
| 29 | +# e.g. $(trap_error_info::_get_caller_line_no) |
| 30 | +trap_error_info::_get_caller_line_no() { |
| 31 | + local level="$1" |
| 32 | + |
| 33 | + # level 0 of caller means self |
| 34 | + # level + 1, to skip `_get_caller_line_no` self |
| 35 | + caller $((level + 1)) | { |
| 36 | + local line_no _ |
| 37 | + read -r line_no _ |
| 38 | + printf "%s" "$line_no" |
| 39 | + } |
| 40 | +} |
| 41 | + |
| 42 | +# show stack trace with format: func name(source file: line no) |
| 43 | +# |
| 44 | +# usage: |
| 45 | +# trap_error_info::show_stack_trace <hide level> <indent> |
| 46 | +# |
| 47 | +# about hide level, default contains 2 extra level of implementation: |
| 48 | +# - trap_error_info::show_stack_trace |
| 49 | +# - trap_error_info::_get_caller_line_no |
| 50 | +# set hide level to 2, hide this 2 xtra level of implementation. |
| 51 | +# |
| 52 | +# do NOT call this function in sub-shell! |
| 53 | +trap_error_info::show_stack_trace() { |
| 54 | + local hide_level="${1:-0}" indent="${2:-}" |
| 55 | + local func_stack_size="${#FUNCNAME[@]}" |
| 56 | + |
| 57 | + local i |
| 58 | + for ((i = hide_level; i < func_stack_size; i++)); do |
| 59 | + printf "%s%s(%s:" "$indent" "${FUNCNAME[i]}" "${BASH_SOURCE[i]}" |
| 60 | + trap_error_info::_get_caller_line_no "$((i - 1))" |
| 61 | + printf ")\n" |
| 62 | + done |
| 63 | +} |
| 64 | + |
| 65 | +# official document of `Bash Variables`, e.g. |
| 66 | +# BASH_SOURCE |
| 67 | +# BASH_LINENO |
| 68 | +# LINENO |
| 69 | +# BASH_COMMAND |
| 70 | +# https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html |
| 71 | +# |
| 72 | +# related info: |
| 73 | +# |
| 74 | +# https://stackoverflow.com/questions/6928946/mysterious-lineno-in-bash-trap-err |
| 75 | +# https://stackoverflow.com/questions/64786/error-handling-in-bash |
| 76 | +# https://stackoverflow.com/questions/24398691/how-to-get-the-real-line-number-of-a-failing-bash-command |
| 77 | +# https://unix.stackexchange.com/questions/39623/trap-err-and-echoing-the-error-line |
| 78 | +# https://unix.stackexchange.com/questions/462156/how-do-i-find-the-line-number-in-bash-when-an-error-occured |
| 79 | +# https://unix.stackexchange.com/questions/365113/how-to-avoid-error-message-during-the-execution-of-a-bash-script |
| 80 | +# https://shapeshed.com/unix-exit-codes/#how-to-suppress-exit-statuses |
| 81 | +# https://stackoverflow.com/questions/30078281/raise-error-in-a-bash-script/50265513#50265513 |
| 82 | +trap_error_info::_show_trapped_error_info() { |
| 83 | + echo "$@" |
| 84 | + local exit_code="$1" error_code_line="$2" |
| 85 | + |
| 86 | + { |
| 87 | + echo '================================================================================' |
| 88 | + echo "Trapped error!" |
| 89 | + echo |
| 90 | + echo "Exit status code: $exit_code" |
| 91 | + echo "Stack trace:" |
| 92 | + trap_error_info::show_stack_trace 2 " " |
| 93 | + echo "Error code line:" |
| 94 | + echo " $error_code_line" |
| 95 | + echo '================================================================================' |
| 96 | + } >&2 |
| 97 | +} |
| 98 | + |
| 99 | +trap_error_info::register_show_error_info_handler() { |
| 100 | + trap 'trap_error_info::_show_trapped_error_info $? "$BASH_COMMAND"' ERR |
| 101 | +} |
| 102 | + |
| 103 | +################################################################################ |
| 104 | +# auto register_show_error_info_handler when source this script; |
| 105 | +# disable by define `TRAP_ERROR_NO_AUTO_REGISTER` var |
| 106 | +################################################################################ |
| 107 | + |
| 108 | +if [ -z "${TRAP_ERROR_NO_AUTO_REGISTER+defined}" ]; then |
| 109 | + trap_error_info::register_show_error_info_handler |
| 110 | +fi |
0 commit comments