From 75be83eb94781a7e56288a082e33dedbb01ac65a Mon Sep 17 00:00:00 2001 From: pvincent Date: Wed, 29 Jul 2026 17:48:03 +0400 Subject: [PATCH] almost done with command_not_found --- poc | 69 ++++++++++++++++++++++++++++++++++++---------- test/poc.test.bash | 6 ++-- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/poc b/poc index 428ecdb..a817402 100755 --- a/poc +++ b/poc @@ -49,20 +49,23 @@ function is_true { # example: alias toto="bash -c \"[[ \\\$MIAOU_DEBUG == 'true' ]] && echo yes \\\$* || echo nope\"" function miaou_debug { if is_true "${MIAOU_DEBUG}"; then + local i message="$*" padding='' local compute_hierarchy=true [[ ! -v miaou_debug_hierarchy ]] && miaou_debug_hierarchy=0 - local i message="$*" padding='' + regex='^&4 builtin echo -e "\e[90m <== DEBUG ==> ${padding}\e[33m${message}\e[0m" + regex='[^/]>$' # does NOT end with /> is_true $compute_hierarchy && [[ "${message}" =~ $regex ]] && - ((++miaou_debug_hierarchy)) || + miaou_debug_hierarchy=$((miaou_debug_hierarchy + 1)) || true fi } @@ -117,7 +120,15 @@ function print_miaou_error { local message message="${source}: line ${line}: miaou_error: ${type} ${code}" [[ -n "$payload" ]] && message+=" payload: ${payload}" - [[ -v tmp_error ]] && miaou_debug "tmp_error=${#tmp_error[@]}" && tmp_error+=("$message") || >&2 echo "$message" + >&2 echo "$message" +} + +function add_miaou_error { + local source="$1" line="$2" code="$3" type="$4" payload="${5:-}" + local message + message="${source}: line ${line}: miaou_error: ${type} ${code}" + [[ -n "$payload" ]] && message+=" payload: ${payload}" + tmp_error+=("$message") } function on_error { @@ -131,11 +142,28 @@ function on_error { if [[ ! -v failure_type ]]; then if [[ ${BASH_COMMAND} == 'false' ]]; then failure_type="FALSE" + print_miaou_error "${BASH_SOURCE[1]}" "${BASH_LINENO[0]}" "${failure_code}" "${failure_type}" else failure_type="COMMAND_ERROR" failure_payload="${BASH_COMMAND}" + print_miaou_error "${BASH_SOURCE[1]}" "${BASH_LINENO[0]}" "${failure_code}" "${failure_type}" "${failure_payload}" + + miaou_debug "" + load_tmp_error # cannot print_miaou_error after investigation + + if [[ ${#tmp_error[@]} -gt 1 ]]; then + local last_error regex + last_error="${tmp_error[-2]}" # the second latest cause previous print_miaou_error above + + regex='^(.*): line ([0-9]+): (.*): command not found$' + if [[ $failure_code == 127 && "$last_error" =~ $regex ]]; then + failure_type='COMMAND_NOT_FOUND' + failure_payload="${BASH_REMATCH[3]}" + miaou_debug "" + fi + fi + miaou_debug '' fi - print_miaou_error "${BASH_SOURCE[1]}" "${BASH_LINENO[0]}" "${failure_code}" "${failure_type}" fi error_source=("${BASH_SOURCE[@]}") @@ -153,11 +181,10 @@ function on_error { else miaou_debug "" fi - builtin exit "$failure_code" else - miaou_debug "" print_miaou_error "${BASH_SOURCE[1]}" "${BASH_LINENO[0]}" "${failure_code}" 'SUBSHELL_ERROR' "${BASH_COMMAND}" + miaou_debug "" kill -PIPE $$ fi } @@ -174,7 +201,7 @@ function on_pipe { unset 'error_source[-1]' 'error_lineno[-1]' 'error_funcname[-1]' unset 'error_source[-1]' 'error_lineno[-1]' 'error_funcname[-1]' - miaou_debug '' && mapfile -t tmp_error <"$MIAOU_BASH_ERROR" + load_tmp_error last_error="${tmp_error[-1]}" regex='^(.*): line ([0-9]+): miaou_error: SUBSHELL_ERROR ([0-9]+) payload: (.*)$' @@ -247,15 +274,24 @@ function dump_stacktrace { miaou_debug '' } +function load_tmp_error { + [[ -v tmp_error ]] && return 0 + + mapfile -t tmp_error <"$MIAOU_BASH_ERROR" + local count=${#tmp_error[@]} + if [[ $count == 0 ]]; then + miaou_debug "" + else + miaou_debug "" + fi +} + function on_exit { trap - RETURN local code="${1:-0}" last_error regex - miaou_debug "" - [[ ! -v tmp_error ]] && - miaou_debug '' && - mapfile -t tmp_error <"$MIAOU_BASH_ERROR" + load_tmp_error if [[ -v failure_type && "$failure_type" == 'RETURN' ]]; then last_error="${tmp_error[-1]}" @@ -272,7 +308,7 @@ function on_exit { miaou_debug "" fi - else + elif [[ ! -v failure_type ]]; then if [[ $code -gt 0 && ${#tmp_error[@]} -gt 0 ]]; then last_error="${tmp_error[-1]}" miaou_debug "" @@ -298,9 +334,10 @@ function on_exit { failure_type='UNBOUND_VARIABLE' failure_payload=$payload - print_miaou_error "${BASH_SOURCE[1]}" "${BASH_LINENO[0]}" "${failure_code}" "${failure_type}" "${failure_payload}" - unset 'tmp_error[-2]' # replace previous last_error + add_miaou_error "${BASH_SOURCE[1]}" "${BASH_LINENO[0]}" "${failure_code}" "${failure_type}" "${failure_payload}" miaou_debug "" + else + failure_type='UNKNOWN ERROR' fi miaou_debug "" fi @@ -332,6 +369,8 @@ trap 'on_exit $?' EXIT trap 'on_pipe' PIPE trap 'on_return' RETURN +declare -a tmp_error # to prevent loading multiple times + if [[ -e /proc/$$/fd/3 ]]; then exec 4>&3 3>&-; else exec 4>/dev/tty; fi # swap fd 3 4 when fd 3 sent off exec 3>/dev/stderr 2>"$MIAOU_BASH_ERROR" diff --git a/test/poc.test.bash b/test/poc.test.bash index 8be33eb..45b2dc0 100755 --- a/test/poc.test.bash +++ b/test/poc.test.bash @@ -24,7 +24,7 @@ function fail_type { mapfile -t stderr <<<"$(./poc ./test/stub/scenario.bash "$1" 2>&1 >/dev/null 3>/dev/null)" last_error="${stderr[-1]}" - regex="^(.*): line ([0-9]+): miaou_error: $2 ([0-9]+) payload: (.*)\$" + regex="^(.*): line ([0-9]+): miaou_error: $2 ([0-9]+)" [[ "${last_error}" =~ $regex ]] && success=true # if [[ $success == true && -n "${hint_message}" ]]; then @@ -60,13 +60,13 @@ fail_type command_not_found COMMAND_NOT_FOUND # fail_type subshell_error COMMAND_NOT_FOUND '(hint: prefer source than subshell)' # fail_type command_error COMMAND_ERROR -failed=$((test_count - ${#failures[@]})) +failed=${#failures[@]} success=$((test_count - failed)) echo if [[ $failed -gt 0 ]]; then ratio=$((100 * success / test_count)) - echo "${ratio}% passed, $success succeeded, $failed failed!" + echo "${ratio}% passed, ${test_count} tests, ${failed} failed!" echo failures: for failure in "${failures[@]}"; do echo -e "\t- $failure"