¿Cómo encuentro el número de línea en Bash cuando ocurre un error?

21

¿Cómo encuentra el número de línea en Bash donde ocurrió un error?

Ejemplo

Creo el siguiente script simple con números de línea para explicar lo que necesitamos. El script copiará archivos de

cp $file1 $file2
cp $file3 $file4

Cuando uno de los cpcomandos falla, la función saldrá con la salida 1 . Queremos agregar la capacidad a la función para imprimir también el error con el número de línea (por ejemplo, 8 o 12).

es posible?

Script de muestra

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
fuente
Puede usar set -xy / o set -vrastrear lo que se ha ejecutado. No es exactamente lo que pediste, pero probablemente también sea útil.
Rolf el

Respuestas:

29

En lugar de usar su función, usaría este método en su lugar:

$ 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"

Esto funciona atrapando en ERR y luego llamando a la failure()función con el número de línea actual + comando bash que se ejecutó.

Ejemplo

Aquí no he tomado ningún cuidado para crear los archivos, f1, f2, f3, o f4. Cuando ejecuto el script anterior:

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

Falla, informando el número de línea más el comando que se ejecutó.

slm
fuente
14

Además de LINENOcontener el número de línea actual, existen las matrices BASH_LINENOy FUNCNAME(y BASH_SOURCE) que contienen los nombres de función y los números de línea desde los que se llama.

Entonces podrías hacer algo como esto:

#!/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

Ejecutando eso imprimiría

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

Si usa set -e, o trap ... ERRpara detectar errores automáticamente, tenga en cuenta que tienen algunas advertencias. También es más difícil incluir una descripción de lo que estaba haciendo el script en ese momento (como lo hizo en su ejemplo), aunque eso podría ser más útil para un usuario normal que solo el número de línea.

Vea, por ejemplo, estos para los problemas con set -ey otros:

ilkkachu
fuente
13

Bash tiene una variable incorporada $LINENOque se reemplaza por el número de línea actual cuando está en una declaración, por lo que puede hacer

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

También puede intentar usar trap ... ERRqué se ejecuta cuando falla un comando (si el resultado no se prueba). P.ej:

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

Luego, si un comando como cp $file1 $file2falla, recibirá el mensaje de error con el número de línea y una salida. También encontrará el comando por error en variable $BASH_COMMAND(aunque no en ninguna redirección, etc.).

meuh
fuente