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

16

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

Exemplo

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

cp $file1 $file2
cp $file3 $file4

Quando um dos comandos cp falhar, a função sairá com exit 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
    
por yael 12.08.2018 / 19:18

3 respostas

25

Em vez de usar sua função, eu usaria esse 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 ERR e chamando a função failure() com o comando atual line number + bash que foi executado.

Exemplo

Aqui, não tomei o cuidado de criar os arquivos f1 , f2 , f3 ou f4 . Quando eu executo o script acima:

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

Ele falha, informando o número da linha mais o comando que foi executado.

    
por 12.08.2018 / 19:23
13

Além de LINENO contendo o número da linha atual, há as matrizes BASH_LINENO e FUNCNAME (e BASH_SOURCE ) que contêm os nomes de função e os números de linha de onde 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 imprime

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

Se você usar set -e ou trap ... ERR para detectar erros automaticamente, observe que eles têm algumas advertências. 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 regular do que apenas o número da linha.

Ver, por exemplo, estes para os problemas com set -e e outros:

por 12.08.2018 / 21:00
10

O Bash tem uma variável interna $LINENO que é 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 trap ... ERR , que é 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 $file2 falhar, 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.).

    
por 12.08.2018 / 20:22