¿Es posible obtener desde qué línea se envió la señal ERR?
Sí, LINENOy las BASH_LINENOvariables son útiles para obtener la línea de falla y las líneas que conducen a ella.
¿O tal vez me estoy yendo mal?
No, solo falta la -qopción con grep ...
echo hello | grep -q "asdf"
... Con la -qopción grepvolverá 0por truey 1para false. Y en Bash es trapno Trap...
trap "_func" ERR
... Necesito una solución nativa ...
Aquí hay un trampero que puede ser útil para depurar cosas que tienen un poco más de complejidad ciclomática ...
failure.sh
## Outputs Front-Mater formatted failures for functions not returning 0
## Use the following line after sourcing this file to set failure trap
## trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR
failure(){
local -n _lineno="${1:-LINENO}"
local -n _bash_lineno="${2:-BASH_LINENO}"
local _last_command="${3:-${BASH_COMMAND}}"
local _code="${4:-0}"
## Workaround for read EOF combo tripping traps
if ! ((_code)); then
return "${_code}"
fi
local _last_command_height="$(wc -l <<<"${_last_command}")"
local -a _output_array=()
_output_array+=(
'---'
"lines_history: [${_lineno} ${_bash_lineno[*]}]"
"function_trace: [${FUNCNAME[*]}]"
"exit_code: ${_code}"
)
if [[ "${#BASH_SOURCE[@]}" -gt '1' ]]; then
_output_array+=('source_trace:')
for _item in "${BASH_SOURCE[@]}"; do
_output_array+=(" - ${_item}")
done
else
_output_array+=("source_trace: [${BASH_SOURCE[*]}]")
fi
if [[ "${_last_command_height}" -gt '1' ]]; then
_output_array+=(
'last_command: ->'
"${_last_command}"
)
else
_output_array+=("last_command: ${_last_command}")
fi
_output_array+=('---')
printf '%s\n' "${_output_array[@]}" >&2
exit ${_code}
}
... y un script de uso de ejemplo para exponer las diferencias sutiles en cómo configurar la trampa anterior para el rastreo de funciones también ...
example_usage.sh
#!/usr/bin/env bash
set -E -o functrace
## Optional, but recommended to find true directory this script resides in
__SOURCE__="${BASH_SOURCE[0]}"
while [[ -h "${__SOURCE__}" ]]; do
__SOURCE__="$(find "${__SOURCE__}" -type l -ls | sed -n 's@^.* -> \(.*\)@\1@p')"
done
__DIR__="$(cd -P "$(dirname "${__SOURCE__}")" && pwd)"
## Source module code within this script
source "${__DIR__}/modules/trap-failure/failure.sh"
trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR
something_functional() {
_req_arg_one="${1:?something_functional needs two arguments, missing the first already}"
_opt_arg_one="${2:-SPAM}"
_opt_arg_two="${3:0}"
printf 'something_functional: %s %s %s' "${_req_arg_one}" "${_opt_arg_one}" "${_opt_arg_two}"
## Generate an error by calling nothing
"${__DIR__}/nothing.sh"
}
## Ignoring errors prevents trap from being triggered
something_functional || echo "Ignored something_functional returning $?"
if [[ "$(something_functional 'Spam!?')" == '0' ]]; then
printf 'Nothing somehow was something?!\n' >&2 && exit 1
fi
## And generating an error state will cause the trap to _trace_ it
something_functional '' 'spam' 'Jam'
Lo anterior se probó en Bash versión 4+, así que deje un comentario si necesita algo para versiones anteriores a la cuatro, o abra un problema si no logra atrapar fallas en sistemas con una versión mínima de cuatro.
Las principales conclusiones son ...
set -E -o functrace
trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR
Las comillas simples se usan alrededor de la llamada de función y las comillas dobles se usan alrededor de argumentos individuales.
Las referencias LINENOy BASH_LINENOse pasan en lugar de los valores actuales, aunque esto podría acortarse en versiones posteriores de vinculados a trap, de modo que la línea de falla final llegue a la salida
Se pasan los valores BASH_COMMANDy el estado de salida ( $?), primero para obtener el comando que devolvió un error, y segundo para garantizar que la trampa no se active en estados sin error
Y aunque otros pueden estar en desacuerdo, creo que es más fácil construir una matriz de salida y usar printf para imprimir cada elemento de la matriz en su propia línea ...
printf '%s\n' "${_output_array[@]}" >&2
... también el >&2bit al final hace que los errores vayan a donde deberían (error estándar), y permite capturar solo errores ...
## ... to a file...
some_trapped_script.sh 2>some_trapped_errros.log
## ... or by ignoring standard out...
some_trapped_script.sh 1>/dev/null
Como se muestra en estos y otros ejemplos en Stack Overflow, hay muchas formas de crear una ayuda de depuración utilizando utilidades integradas.
bashdb. Parece que el primer argumentotrappuede contener variables que se evalúan en el contexto deseado. Entoncestrap 'echo $LINENO' ERR'debería funcionar.