Como localizo o número da linha no Bash quando ocorreu um erro?

21

Como você encontra o número da linha no Bash onde ocorreu um erro?

Exemplo

Crio o seguinte script simples com números de linha para explicar o que precisamos. O script copiará arquivos de

cp $file1 $file2
cp $file3 $file4

Quando um dos cpcomandos falha, a função sai com a saída 1 . Queremos adicionar a capacidade da função de também imprimir o erro com o número da linha (por exemplo, 8 ou 12).

Isso é possível?

Script de amostra

1 #!/bin/bash
2
3
4 function in_case_fail {
5 [[ $1 -ne 0 ]] && echo "fail on $2" && exit 1
6 }
7
8 cp $file1 $file2
9 in_case_fail $? "cp $file1 $file2"
10
11
12 cp $file3 $file4
13 in_case_fail $? "cp $file3 $file4"
14
yael
fonte
Você pode usar set -xe / ou set -vrastrear o que foi executado. Não é exatamente o que você pediu, mas provavelmente será útil também.
Rolf

Respostas:

29

Em vez de usar sua função, eu usaria este método:

$ cat yael.bash
#!/bin/bash

set -eE -o functrace

file1=f1
file2=f2
file3=f3
file4=f4

failure() {
  local lineno=$1
  local msg=$2
  echo "Failed at $lineno: $msg"
}
trap 'failure ${LINENO} "$BASH_COMMAND"' ERR

cp -- "$file1" "$file2"
cp -- "$file3" "$file4"

Isso funciona interceptando o erro e, em seguida, chamando a failure()função com o número da linha atual + comando bash que foi executado.

Exemplo

Aqui eu não tenho tido todo o cuidado para criar os arquivos, f1, f2, f3, ou f4. Quando executo o script acima:

$ ./yael.bash
cp: cannot stat f1’: No such file or directory
Failed at 17: cp -- "$file1" "$file2"

Falha, relatando o número da linha mais o comando que foi executado.

slm
fonte
14

Além de LINENOconter o número da linha atual, existem as matrizes BASH_LINENOe FUNCNAME(e BASH_SOURCE) que contêm os nomes das funções e os números de linha dos quais são chamados.

Então você poderia fazer algo assim:

#!/bin/bash

error() {
        printf "'%s' failed with exit code %d in function '%s' at line %d.\n" "${1-something}" "$?" "${FUNCNAME[1]}" "${BASH_LINENO[0]}"
}

foo() {
        ( exit   0 ) || error "this thing"
        ( exit 123 ) || error "that thing"
}

foo

Execução que imprimiria

'that thing' failed with exit code 123 in function 'foo' at line 9.

Se você usar set -eou trap ... ERRdetectar automaticamente erros, observe que eles têm algumas ressalvas. Também é mais difícil incluir uma descrição do que o script estava fazendo no momento (como você fez no seu exemplo), embora isso possa ser mais útil para um usuário comum do que apenas o número da linha.

Veja, por exemplo, estes para os problemas com set -ee outros:

ilkkachu
fonte
13

O Bash possui uma variável interna $LINENOque é substituída pelo número da linha atual quando em uma instrução, para que você possa fazer

in_case_fail $? "at $LINENO: cp $file1 $file2"

Você também pode tentar usar o trap ... ERRque é executado quando um comando falha (se o resultado não for testado). Por exemplo:

trap 'rc=$?; echo "error code $rc at $LINENO"; exit $rc' ERR

Então, se um comando como cp $file1 $file2falhar, você receberá a mensagem de erro com o número da linha e uma saída. Você também encontrará o comando com erro na variável $BASH_COMMAND(embora não haja redirecionamentos, etc.).

meuh
fonte