#!/usr/bin/env bash # CONSTANTS DEBUG=true TMP_ERROR=$(mktemp -t "$(basename $0).XXXXXXXX" --tmpdir=/run/user/$(id -u)) SCRIPT=$1 # FUNCTIONS # overridden function which permits stacktracing of original `return` statement function return { code=${1:-0} [[ $code -gt 0 ]] && >&2 echo "${BASH_SOURCE[2]}: line ${BASH_LINENO[0]}: return $@" builtin return $code } # overridden function which permits stacktracing of original `return` statement function exit { code=${1:-0} >&2 echo "${BASH_SOURCE[2]}: line ${BASH_LINENO[0]}: exit $@" builtin exit $code } function on_exit { code=${1:-0} >&4 dump_stderr stack_lines=("${BASH_LINENO[@]}") stack_functions=("${FUNCNAME[@]}") stack_sources=("${BASH_SOURCE[@]}") unset stack_lines[-1] unset stack_lines[-1] unset stack_functions[0] unset stack_functions[-1] stack_functions[-1]='
' unset stack_sources[0] unset stack_sources[-1] stack_lines=("${stack_lines[@]}") stack_functions=("${stack_functions[@]}") stack_sources=("${stack_sources[@]}") if [[ $code -gt 0 ]]; then $DEBUG && >/dev/tty echo '---stacktrace---' last_error=$(tail -n1 $TMP_ERROR) last_source=${stack_sources[0]} last_source=${last_source//./"\."} # replace . by \. regex="^$last_source: line ([0-9]+): (.*)$" if [[ "$last_error" =~ $regex ]]; then line=${BASH_REMATCH[1]} message=${BASH_REMATCH[2]} regex=': unbound variable$' [[ $code == 1 ]] && [[ "$message" =~ $regex ]] && stack_lines[0]=$line >&4 echo -e "ERROR $code: [$message]" else >&4 echo "ERROR $code: " fi for i in "${!stack_functions[@]}"; do >&4 printf "\t%30s\t%s\n" "${stack_sources[$i]}:${stack_lines[$i]}" "${stack_functions[$i]}" done fi # echo "${stack_lines[@]}" # echo "${stack_functions[@]}" # echo "${stack_sources[@]}" cleanup builtin exit $code } function dump_stderr { if [[ -s $TMP_ERROR ]]; then $DEBUG && >/dev/tty echo '---stderr---' cat $TMP_ERROR fi } function success { >&2 dump_stderr cleanup } function cleanup { trap - ERR TERM INT EXIT exec 3>&- 4>&- rm $TMP_ERROR } # MAIN set -TEue -o pipefail trap 'on_exit $?' ERR TERM INT EXIT # magic FD exec 3>$TMP_ERROR 4>/dev/stderr $DEBUG && >/dev/tty echo '---stdout---' source $SCRIPT 2>&3 success