web-dev-qa-db-fra.com

Comment trouver le numéro de ligne dans Bash lorsqu'une erreur s'est produite?

Comment trouvez-vous le numéro de ligne dans Bash où une erreur s'est produite?

Exemple

Je crée le script simple suivant avec des numéros de ligne pour expliquer ce dont nous avons besoin. Le script copiera les fichiers de

cp $file1 $file2
cp $file3 $file4

Lorsque l'une des commandes cp échoue, la fonction se termine avec exit 1. Nous voulons ajouter la possibilité à la fonction d'imprimer également l'erreur avec le numéro de ligne (par exemple, 8 ou 12).

Est-ce possible?

Exemple de script

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
23
yael

Plutôt que d'utiliser votre fonction, j'utiliserais plutôt cette méthode:

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

Cela fonctionne en interceptant ERR puis en appelant la fonction failure() avec la ligne numéro actuelle + la commande bash qui a été exécutée.

Exemple

Ici, je n'ai pas pris soin de créer les fichiers, f1, f2, f3 Ou f4. Lorsque j'exécute le script ci-dessus:

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

Il échoue, signalant le numéro de ligne plus la commande qui a été exécutée.

32
slm

En plus de LINENO contenant le numéro de ligne actuel, il y a BASH_LINENO et FUNCNAME (et BASH_SOURCE) tableaux contenant les noms de fonction et les numéros de ligne à partir desquels ils sont appelés.

Vous pouvez donc faire quelque chose comme ceci:

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

Courir qui imprimerait

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

Si tu utilises set -e, ou trap ... ERR pour détecter automatiquement les erreurs, notez qu'elles comportent des mises en garde. Il est également plus difficile d'inclure une description de ce que le script faisait à l'époque (comme vous l'avez fait dans votre exemple), bien que cela puisse être plus utile pour un utilisateur normal que le simple numéro de ligne.

Voir par exemple ceux-ci pour les problèmes avec set -e et d'autres:

15
ilkkachu

Bash a une variable intégrée $LINENO qui est remplacé par le numéro de ligne actuel dans une instruction, vous pouvez donc

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

Vous pouvez également essayer d'utiliser trap ... ERR qui s'exécute lorsqu'une commande échoue (si le résultat n'est pas testé). Par exemple:

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

Ensuite, si une commande comme cp $file1 $file2 échoue, vous obtiendrez le message d'erreur avec le numéro de ligne et une sortie. Vous trouverez également la commande en erreur dans la variable $BASH_COMMAND (mais pas de redirections, etc.).

14
meuh