Quelle est votre méthode préférée pour gérer les erreurs dans Bash? Le meilleur exemple de gestion des erreurs que j'ai trouvé sur le Web a été écrit par William Shotts, Jr à http://www.linuxcommand.org .
Il suggère d'utiliser la fonction suivante pour la gestion des erreurs dans Bash:
#!/bin/bash
# A slicker error handling routine
# I put a variable in my scripts named PROGNAME which
# holds the name of the program being run. You can get this
# value from the first item on the command line ($0).
# Reference: This was copied from <http://www.linuxcommand.org/wss0150.php>
PROGNAME=$(basename $0)
function error_exit
{
# ----------------------------------------------------------------
# Function for exit due to fatal program error
# Accepts 1 argument:
# string containing descriptive error message
# ----------------------------------------------------------------
echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
exit 1
}
# Example call of the error_exit function. Note the inclusion
# of the LINENO environment variable. It contains the current
# line number.
echo "Example of error with line number and message"
error_exit "$LINENO: An error has occurred."
Avez-vous une meilleure routine de gestion des erreurs que vous utilisez dans les scripts Bash?
Réponses:
Utilisez un piège!
... puis, chaque fois que vous créez un fichier temporaire:
et
$temp_foo
sera supprimé à la sortie, et le numéro de ligne actuel sera imprimé. (set -e
vous donnera également un comportement de sortie sur erreur, bien qu'il s'accompagne de sérieuses mises en garde et affaiblit la prévisibilité et la portabilité du code).Vous pouvez laisser le piège vous appeler
error
(auquel cas il utilise le code de sortie par défaut 1 et aucun message) ou l'appeler vous-même et fournir des valeurs explicites; par exemple:quittera avec le statut 2 et donnera un message explicite.
la source
Voilà une bonne solution. Je voulais juste ajouter
comme mécanisme d'erreur rudimentaire. Il arrêtera immédiatement votre script si une simple commande échoue. Je pense que cela aurait dû être le comportement par défaut: comme de telles erreurs signifient presque toujours quelque chose d'inattendu, il n'est pas vraiment «sensé» de continuer à exécuter les commandes suivantes.
la source
set -e
n'est pas sans accrochages: Voir mywiki.wooledge.org/BashFAQ/105 pour plusieurs.set -o pipefail
set -e
avoir un rapport avantages-coûts élevé.set -e
même, mais un certain nombre d'autres habitués de irc.freenode.org # bash le déconseillent (en termes assez forts). Au minimum, les pièges en question doivent être bien compris.La lecture de toutes les réponses sur cette page m'a beaucoup inspiré.
Voici donc mon indice:
contenu du fichier: lib.trap.sh
Exemple d'utilisation:
contenu du fichier: trap-test.sh
Fonctionnement:
Production:
Comme vous pouvez le voir sur la capture d'écran ci-dessous, la sortie est colorée et le message d'erreur vient dans la langue utilisée.
la source
test ${#g_libs[@]} == 0
n'est pas compatible POSIX (le test POSIX prend en charge=
les comparaisons de chaînes ou-eq
les comparaisons numériques, mais pas==
, sans parler du manque de tableaux dans POSIX), et si vous n'essayez pas d'être conforme POSIX, pourquoi dans le monde utilisez-voustest
plutôt que d'un contexte mathématique?(( ${#g_libs[@]} == 0 ))
est, après tout, plus facile à lire.case "$(uname)" in Darwin ) stderr_log="${TMPDIR}stderr.log";; Linux ) stderr_log="/dev/shm/stderr.log";; * ) stderr_log="/dev/shm/stderr.log" ;; esac
Une alternative équivalente à "set -e" est
Cela rend la signification du drapeau un peu plus claire que le simple "-e".
Ajout aléatoire: pour désactiver temporairement l'indicateur et revenir à la valeur par défaut (de continuer l'exécution indépendamment des codes de sortie), utilisez simplement
Cela empêche la gestion correcte des erreurs mentionnée dans d'autres réponses, mais est rapide et efficace (tout comme bash).
la source
$(foo)
sur une ligne nue plutôt que justefoo
est généralement la mauvaise chose. Pourquoi le promouvoir en le donnant comme exemple?Inspiré par les idées présentées ici, j'ai développé un moyen lisible et pratique de gérer les erreurs dans les scripts bash dans mon projet bash passe-partout .
En achetant simplement la bibliothèque, vous obtenez ce qui suit de la boîte (c'est-à-dire qu'il arrêtera l'exécution sur toute erreur, comme si vous l'utilisiez
set -e
grâce à untrap
onERR
et à certains bash-fu ):Il existe des fonctionnalités supplémentaires qui aident à gérer les erreurs, telles que try and catch ou le mot clé throw , qui vous permettent d'interrompre l'exécution à un moment pour voir la trace. De plus, si le terminal le prend en charge, il crache des emojis Powerline, colore des parties de la sortie pour une grande lisibilité et souligne la méthode qui a provoqué l'exception dans le contexte de la ligne de code.
L'inconvénient est - ce n'est pas portable - le code fonctionne en bash, probablement> = 4 seulement (mais j'imagine qu'il pourrait être porté avec un certain effort pour bash 3).
Le code est séparé en plusieurs fichiers pour une meilleure manipulation, mais j'ai été inspiré par l'idée de retour en arrière de la réponse ci-dessus de Luca Borrione .
Pour en savoir plus ou consulter la source, consultez GitHub:
https://github.com/niieani/bash-oo-framework#error-handling-with-exceptions-and-throw
la source
Je préfère quelque chose de très simple à appeler. J'utilise donc quelque chose qui a l'air un peu compliqué, mais qui est facile à utiliser. Je copie et collez généralement le code ci-dessous dans mes scripts. Une explication suit le code.
Je mets généralement un appel à la fonction de nettoyage à côté de la fonction error_exit, mais cela varie d'un script à l'autre, donc je l'ai laissé de côté. Les pièges captent les signaux de terminaison communs et s'assurent que tout est nettoyé. L'alias est ce qui fait la vraie magie. J'aime tout vérifier l'échec. Donc, en général, j'appelle les programmes dans un "si!" déclaration de type. En soustrayant 1 du numéro de ligne, l'alias me dira où la panne s'est produite. Il est également très simple à appeler et à peu près à l'épreuve des idiots. Voici un exemple (remplacez simplement / bin / false par ce que vous allez appeler).
la source
$LINENO - 1
. Montrez correctement sans elle.false || die "hello death"
Une autre considération est le code de sortie à retourner. Juste "
1
" est assez standard, bien qu'il existe une poignée de codes de sortie réservés que bash utilise lui-même , et cette même page soutient que les codes définis par l'utilisateur devraient être compris entre 64 et 113 pour se conformer aux normes C / C ++.Vous pouvez également considérer l'approche de vecteur de bits qui
mount
utilise pour ses codes de sortie:OR
-la combinaison des codes permet à votre script de signaler plusieurs erreurs simultanées.la source
J'utilise le code d'interruption suivant, il permet également de tracer les erreurs via des tuyaux et des commandes «time»
la source
function
mot-clé est incompatible avec POSIX gratuitement. Pensez à faire votre déclaration justeerror() {
, sansfunction
avant.${$?}
devrait être$?
, ou${?}
si vous insistez pour utiliser des accolades inutiles; l'intérieur$
est faux.J'ai utilisé
avant; je pense parce que la «sortie» échouait pour moi pour une raison quelconque. Les valeurs par défaut ci-dessus semblent cependant être une bonne idée.
la source
Cela me sert bien depuis un moment maintenant. Il imprime des messages d'erreur ou d'avertissement en rouge, une ligne par paramètre, et autorise un code de sortie facultatif.
la source
Je ne sais pas si cela vous sera utile, mais j'ai modifié certaines des fonctions suggérées ici afin d'y inclure la vérification de l'erreur (code de sortie de la commande précédente). A chaque "vérification", je passe également en paramètre le "message" de l'erreur à des fins de journalisation.
Maintenant, pour l'appeler dans le même script (ou dans un autre si j'utilise
export -f error_exit
), j'écris simplement le nom de la fonction et je passe un message en paramètre, comme ceci:En utilisant cela, j'ai pu créer un fichier bash vraiment robuste pour un processus automatisé et il s'arrêtera en cas d'erreur et m'informera (le
log.sh
fera)la source
function
mot-clé, justeerror_exit() {
.cd /home/myuser/afolder || error_exit "Unable to switch to folder"
?Cette astuce est utile pour les commandes ou fonctions manquantes. Le nom de la fonction manquante (ou exécutable) sera passé dans $ _
la source
$_
disponible dans la fonction de la même manière que$?
? Je ne suis pas sûr qu'il y ait une raison d'utiliser l'un dans la fonction mais pas l'autre.Cette fonction m'a plutôt bien servi récemment:
Vous l'appelez en ajoutant 0 ou la dernière valeur de retour au nom de la commande à exécuter, vous pouvez donc chaîner des commandes sans avoir à vérifier les valeurs d'erreur. Avec cela, ce bloc de déclaration:
Devient ceci:
Si l'une des commandes échoue, le code d'erreur est simplement transmis à la fin du bloc. Je trouve cela utile lorsque vous ne voulez pas que les commandes suivantes s'exécutent si une précédente a échoué, mais vous ne voulez pas non plus que le script se termine immédiatement (par exemple, dans une boucle).
la source
L'utilisation de trap n'est pas toujours une option. Par exemple, si vous écrivez une sorte de fonction réutilisable qui nécessite une gestion des erreurs et qui peut être appelée à partir de n'importe quel script (après avoir obtenu le fichier avec des fonctions d'assistance), cette fonction ne peut rien supposer de l'heure de sortie du script externe, ce qui rend l'utilisation des pièges très difficile. Un autre inconvénient de l'utilisation des interruptions est une mauvaise composabilité, car vous risquez d'écraser l'interruption précédente qui pourrait être définie plus tôt dans la chaîne d'appel.
Il y a une petite astuce qui peut être utilisée pour faire une gestion correcte des erreurs sans pièges. Comme vous le savez peut-être déjà à partir d'autres réponses,
set -e
ne fonctionne pas à l'intérieur des commandes si vous utilisez l'||
opérateur après elles, même si vous les exécutez dans un sous-shell; par exemple, cela ne fonctionnerait pas:Mais l'
||
opérateur est nécessaire pour empêcher le retour de la fonction externe avant le nettoyage. L'astuce consiste à exécuter la commande interne en arrière-plan, puis à l'attendre immédiatement. La fonctionwait
intégrée renverra le code de sortie de la commande interne, et maintenant vous utilisez||
aprèswait
, pas la fonction interne, doncset -e
fonctionne correctement à l'intérieur de cette dernière:Voici la fonction générique qui s'appuie sur cette idée. Cela devrait fonctionner dans tous les shells compatibles POSIX si vous supprimez des
local
mots clés, c'est-à-dire remplacez toutlocal x=y
par justex=y
:Exemple d'utilisation:
Exécuter l'exemple:
La seule chose dont vous devez être conscient lorsque vous utilisez cette méthode est que toutes les modifications des variables Shell effectuées à partir de la commande à laquelle vous passez
run
ne se propageront pas à la fonction appelante, car la commande s'exécute dans un sous-shell.la source