É possível obter de que linha o sinal ERR foi enviado?
Sim, LINENO
e as BASH_LINENO
variáveis são úteis para obter a linha de falha e as linhas que levam a ela.
Ou talvez eu esteja indo para tudo errado?
Não, apenas falta a -q
opção com grep ...
echo hello | grep -q "asdf"
... Com a -q
opção grep
retornará 0
para true
e 1
para false
. E em Bash é trap
não Trap
...
trap "_func" ERR
... preciso de uma solução nativa ...
Aqui está um captador que você pode achar útil para depurar coisas que têm um pouco mais de complexidade 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}
}
... e um exemplo de script de uso para expor as diferenças sutis de como definir a interceptação acima também para o rastreamento de funções ...
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'
O exemplo acima foi testado no Bash versão 4+, portanto, deixe um comentário se for necessário algo para versões anteriores ao quatro ou Abra um problema se ele não detectar falhas nos sistemas com uma versão mínima de quatro.
Os principais tópicos são ...
set -E -o functrace
trap 'failure "LINENO" "BASH_LINENO" "${BASH_COMMAND}" "${?}"' ERR
Aspas simples são usadas em torno da chamada de função e as aspas duplas em torno de argumentos individuais
Referências LINENO
e BASH_LINENO
são passadas em vez dos valores atuais, embora isso possa ser abreviado em versões posteriores do vinculado à interceptação, de modo que a linha de falha final chegue à saída
Os valores de BASH_COMMAND
e exit status ( $?
) são passados, primeiro para obter o comando que retornou um erro e, segundo, para garantir que a interceptação não seja acionada em status de não erro
E enquanto outros podem discordar, acho mais fácil criar uma matriz de saída e usar printf para imprimir cada elemento da matriz em sua própria linha ...
printf '%s\n' "${_output_array[@]}" >&2
... também o >&2
bit no final faz com que os erros cheguem aonde deveriam (erro padrão) e permite capturar apenas erros ...
## ... to a file...
some_trapped_script.sh 2>some_trapped_errros.log
## ... or by ignoring standard out...
some_trapped_script.sh 1>/dev/null
Conforme mostrado por esses e outros exemplos no Stack Overflow, existem várias maneiras de criar um auxiliar de depuração usando utilitários embutidos.
bashdb
. Parece que o primeiro argumento paratrap
pode conter variáveis que são avaliadas no contexto desejado. Entãotrap 'echo $LINENO' ERR'
deve funcionar.