Est-il possible d'obtenir de quelle ligne le signal ERR a été envoyé?
Oui, LINENO
et les BASH_LINENO
variables sont très utiles pour obtenir la ligne d'échec et les lignes qui y mènent.
Ou peut-être que je me trompe?
Non, il manque juste une -q
option avec grep ...
echo hello | grep -q "asdf"
... Avec l' -q
option grep
reviendra 0
pour true
et 1
pour false
. Et dans Bash ce n'est trap
pas Trap
...
trap "_func" ERR
... j'ai besoin d'une solution native ...
Voici un trappeur que vous pourriez trouver utile pour déboguer des choses qui ont un peu plus de complexité cyclomatique ...
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}
}
... et un exemple de script d'utilisation pour exposer les différences subtiles dans la façon de définir le piège ci-dessus pour le traçage de fonction aussi ...
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'
Ce qui précède a été testé sur Bash version 4+, alors laissez un commentaire si quelque chose pour les versions antérieures à quatre est nécessaire, ou ouvrez un problème s'il ne parvient pas à intercepter les échecs sur les systèmes avec une version minimale de quatre.
Les principaux plats à emporter sont ...
set -E -o functrace
trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR
Les guillemets simples sont utilisés autour de l'appel de fonction et les guillemets doubles sont autour des arguments individuels
Les références à LINENO
et BASH_LINENO
sont passées à la place des valeurs actuelles, bien que cela puisse être raccourci dans les versions ultérieures de Linked to Trap, de sorte que la ligne d'échec finale la rende en sortie
Les valeurs de BASH_COMMAND
et exit status ( $?
) sont transmises, d'abord pour obtenir la commande qui a renvoyé une erreur, et ensuite pour s'assurer que l'interruption ne se déclenche pas sur les états sans erreur
Et tandis que d'autres peuvent être en désaccord, je trouve qu'il est plus facile de créer un tableau de sortie et d'utiliser printf pour imprimer chaque élément du tableau sur sa propre ligne ...
printf '%s\n' "${_output_array[@]}" >&2
... aussi le >&2
bit à la fin fait que les erreurs vont là où elles devraient (erreur standard), et permet de capturer uniquement les erreurs ...
## ... to a file...
some_trapped_script.sh 2>some_trapped_errros.log
## ... or by ignoring standard out...
some_trapped_script.sh 1>/dev/null
Comme le montrent ces exemples et d' autres sur Stack Overflow, il existe de nombreuses façons de créer une aide au débogage à l'aide d'utilitaires intégrés.
bashdb
. Il semble que le premier argument àtrap
contenir puisse contenir des variables évaluées dans le contexte souhaité. Celatrap 'echo $LINENO' ERR'
devrait donc fonctionner.