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 piégeant sur ERR puis en appelant le failure()
fonction avec le numéro de ligne actuel + 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, rapportant le numéro de ligne plus la commande qui a été exécutée.
En plus de LINENO
contenant le numéro de la ligne courante, il y a les BASH_LINENO
et FUNCNAME
(et BASH_SOURCE
) des tableaux contenant les noms de fonction et les numéros de ligne à partir desquels ils sont appelés.
Vous pourriez 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
Exécution qui imprimerait
'that thing' failed with exit code 123 in function 'foo' at line 9.
Si vous utilisez set -e
, ou trap ... ERR
pour détecter automatiquement les erreurs, notez qu'ils ont quelques 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 ordinaire que le simple numéro de ligne.
Voir par ex. ceux-ci pour les problèmes avec set -e
et autres :
- Pourquoi set -e ne fonctionne-t-il pas dans les sous-shells avec des parenthèses () suivies d'une liste OR || ?
- bash -e se termine lorsque let ou expr est évalué à 0
- BashFAQ 105 :Pourquoi set -e (ou set -o errexit, ou trap ERR) ne fait-il pas ce que j'attendais ?
Bash a une variable intégrée $LINENO
qui est remplacé par le numéro de ligne actuel dans une instruction, vous pouvez donc le faire
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é). Ex :
trap 'rc=$?; echo "error code $rc at $LINENO"; exit $rc' ERR
Alors 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.).