1 changed files with 141 additions and 0 deletions
-
141bin/miaou-bash
@ -0,0 +1,141 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
# CONSTANTS |
|||
BASH_BACK_ERROR=$(mktemp -t "$(basename $0).XXXXXXXX" --tmpdir=/run/user/$(id -u)) |
|||
BASH_BACK_DEBUG=${BASH_BACK_DEBUG:-false} |
|||
|
|||
# FUNCTIONS |
|||
|
|||
function on_return { |
|||
if [[ "${BASH_COMMAND}" == 'return '* ]]; then |
|||
code=$(echo "${BASH_COMMAND}" | cut -d' ' -f2) # regex does not work here unfortunately! thus use of basic cut |
|||
if [[ -n "$code" ]] && [[ "$code" -ne 0 ]]; then |
|||
trap - DEBUG |
|||
>&2 echo "${BASH_SOURCE[2]}: line ${BASH_LINENO[0]}: return $code from ${FUNCNAME[1]}" |
|||
fi |
|||
fi |
|||
} |
|||
|
|||
# overridden function which permits stacktracing of original `return` statement |
|||
function exit { |
|||
code=${1:-0} |
|||
>&2 echo "${BASH_SOURCE[2]}:${BASH_LINENO[0]} exit $@" |
|||
# >&2 echo "${BASH_SOURCE[2]}: line ${BASH_LINENO[0]}: exit $@" |
|||
builtin exit $code |
|||
} |
|||
|
|||
function on_exit { |
|||
code=${1:-0} |
|||
|
|||
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]='<main>' |
|||
unset stack_sources[0] |
|||
unset stack_sources[-1] |
|||
|
|||
if [[ "${stack_functions[1]}" == exit && "${stack_sources[1]}" == "${BASH_SOURCE[0]}" ]]; then |
|||
# the exit case scenario |
|||
unset stack_functions[1] |
|||
unset stack_sources[1] |
|||
unset stack_lines[0] |
|||
fi |
|||
|
|||
stack_lines=("${stack_lines[@]}") |
|||
stack_functions=("${stack_functions[@]}") |
|||
stack_sources=("${stack_sources[@]}") |
|||
|
|||
stack_summary=() |
|||
|
|||
if [[ $code -gt 0 ]]; then |
|||
last_error=$(tail -n1 $BASH_BACK_ERROR) |
|||
last_source=${stack_sources[0]} |
|||
last_source=${last_source//./"\."} # replace . by \. |
|||
regex1="^$last_source: line ([0-9]+): (.*)\$" |
|||
regex2="^$last_source:([0-9]+) (.*)\$" |
|||
if [[ "$last_error" =~ $regex1 || "$last_error" =~ $regex2 ]]; then |
|||
line=${BASH_REMATCH[1]} |
|||
message=${BASH_REMATCH[2]} |
|||
regex=': unbound variable$' |
|||
[[ $code == 1 ]] && [[ "$message" =~ $regex ]] && stack_lines[0]=$line |
|||
|
|||
regex="^return ([0-9]*) from (.*)\$" |
|||
if [[ "$message" =~ $regex ]]; then |
|||
return_code="${BASH_REMATCH[1]}" |
|||
funcname="${BASH_REMATCH[2]}" |
|||
# >&4 echo "RETURN DETECTED line ${stack_sources[0]} $line $funcname" |
|||
stack_sources=("${stack_sources[0]}" "${stack_sources[@]}") |
|||
stack_lines=("$line" "${stack_lines[@]}") |
|||
stack_functions=("$funcname" "${stack_functions[@]}") |
|||
stack_summary+=("ERROR $code: [return $return_code]") |
|||
else |
|||
stack_summary+=("ERROR $code: [$message]") |
|||
fi |
|||
else |
|||
regex="^ .*:[0-9]* .*\$" |
|||
[[ "$last_error" =~ $regex ]] || stack_summary+=("ERROR $code:") # <undefined> |
|||
fi |
|||
|
|||
for i in "${!stack_functions[@]}"; do |
|||
stack_detail=$(printf "\t%30s\t%s\n" "${stack_sources[$i]}:${stack_lines[$i]}" "${stack_functions[$i]}") |
|||
stack_summary+=("$stack_detail") |
|||
done |
|||
>&4 dump_stderr true |
|||
dump_summary |
|||
else |
|||
>&4 dump_stderr false |
|||
fi |
|||
|
|||
cleanup |
|||
builtin exit $code |
|||
} |
|||
|
|||
function dump_summary { |
|||
$BASH_BACK_DEBUG && >/dev/tty echo '---stacktrace---' |
|||
for i in "${stack_summary[@]}"; do |
|||
>&4 echo -e "$i" |
|||
done |
|||
} |
|||
|
|||
function dump_stderr { |
|||
last_trim=${1:-false} |
|||
mapfile -t tmp_error <$BASH_BACK_ERROR |
|||
$last_trim && unset tmp_error[-1] && tmp_error=("${tmp_error[@]}") |
|||
if [[ "${#tmp_error[@]}" -gt 0 ]]; then |
|||
$BASH_BACK_DEBUG && >/dev/tty echo '---stderr---' |
|||
for i in "${tmp_error[@]}"; do |
|||
echo -e "$i" |
|||
done |
|||
fi |
|||
} |
|||
|
|||
function success { |
|||
>&2 dump_stderr |
|||
cleanup |
|||
} |
|||
|
|||
function cleanup { |
|||
trap - ERR TERM INT EXIT |
|||
exec 3>&- 4>&- |
|||
rm $BASH_BACK_ERROR |
|||
} |
|||
|
|||
# MAIN |
|||
|
|||
set -TEue -o pipefail |
|||
[[ $# -ne 1 ]] && >&2 echo 'ERROR: script expected!' && builtin exit 1 |
|||
|
|||
trap 'on_exit $?' ERR TERM INT EXIT |
|||
trap 'on_return' DEBUG |
|||
|
|||
# magic FD |
|||
exec 3> >(tee $BASH_BACK_ERROR >/dev/null) 4>/dev/stderr |
|||
|
|||
$BASH_BACK_DEBUG && >/dev/tty echo '---stdout---' |
|||
source $1 2>&3 |
|||
success |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue