Existe-t-il quelque chose de similaire à pipefail pour plusieurs commandes, comme une instruction 'try' mais dans bash. Je voudrais faire quelque chose comme ça:
echo "trying stuff"
try {
command1
command2
command3
}
Et à tout moment, si une commande échoue, abandonnez et répercutez l'erreur de cette commande. Je ne veux pas avoir à faire quelque chose comme:
command1
if [ $? -ne 0 ]; then
echo "command1 borked it"
fi
command2
if [ $? -ne 0 ]; then
echo "command2 borked it"
fi
Et ainsi de suite ... ou quelque chose comme:
pipefail -o
command1 "arg1" "arg2" | command2 "arg1" "arg2" | command3
Parce que les arguments de chaque commande je crois (corrigez-moi si je me trompe) vont interférer les uns avec les autres. Ces deux méthodes me semblent horriblement longues et désagréables, je suis donc ici pour une méthode plus efficace.
set -euo pipefail
.set -e
est une horrible idée. Voir les exercices dans BashFAQ # 105 traitant de quelques-uns des cas marginaux inattendus qu'il introduit, et / ou la comparaison montrant les incompatibilités entre les implémentations de différents shells (et versions de shell) sur in-ulm.de/~mascheck/various/set -e .Réponses:
Vous pouvez écrire une fonction qui lance et teste la commande pour vous. Supposons
command1
etcommand2
sont des variables d'environnement qui ont été définies sur une commande.la source
$*
, il échouera si des arguments contiennent des espaces; utiliser à la"$@"
place. De même, insérez$1
les guillemets dans laecho
commande.test
car c'est une commande intégrée.ls
. Si vous invoquezls foo
et obtenez un message d'erreur du formulaire,ls: foo: No such file or directory\n
vous comprenez le problème. Si au lieu de cela, vous vousls: foo: No such file or directory\nerror with ls\n
laissez distraire par des informations superflues. Dans ce cas, il est assez facile de faire valoir que le superflu est trivial, mais il se développe rapidement. Des messages d'erreur concis sont importants. Mais plus important encore, ce type de wrapper encourage également les rédacteurs à omettre complètement les bons messages d'erreur.Qu'entendez-vous par «abandonner et répéter l'erreur»? Si vous voulez dire que vous voulez que le script se termine dès qu'une commande échoue, faites simplement
au début du script (mais notez l'avertissement ci-dessous). Ne vous embêtez pas à faire écho au message d'erreur: laissez la commande défaillante gérer cela. En d'autres termes, si vous le faites:
et command2 échoue, lors de l'impression d'un message d'erreur sur stderr, il semble que vous ayez atteint ce que vous vouliez. (À moins que j'interprète mal ce que vous voulez!)
En corollaire, toute commande que vous écrivez doit se comporter correctement: elle doit signaler les erreurs à stderr au lieu de stdout (l'exemple de code dans la question imprime les erreurs à stdout) et elle doit quitter avec un état différent de zéro lorsqu'elle échoue.
Cependant, je ne considère plus cela comme une bonne pratique.
set -e
a changé sa sémantique avec différentes versions de bash, et bien que cela fonctionne bien pour un script simple, il y a tellement de cas marginaux qu'il est essentiellement inutilisable. (Considérez des choses comme:set -e; foo() { false; echo should not print; } ; foo && echo ok
la sémantique ici est quelque peu raisonnable, mais si vous refactorisez le code en une fonction qui reposait sur le paramètre d'option pour se terminer tôt, vous pouvez facilement être mordu.) IMO, il est préférable d'écrire:ou
la source
trap some_func 0
, s'exécuterasome_func
à la sortie)|| exit
explicitement après chaque commande.J'ai un ensemble de fonctions de script que j'utilise intensivement sur mon système Red Hat. Ils utilisent les fonctions système de
/etc/init.d/functions
pour imprimer des indicateurs d'état verts[ OK ]
et rouges[FAILED]
.Vous pouvez éventuellement définir la
$LOG_STEPS
variable sur un nom de fichier journal si vous souhaitez enregistrer les commandes qui échouent.Usage
Production
Code
la source
Pour ce que ça vaut, une façon plus courte d'écrire du code pour vérifier le succès de chaque commande est:
C'est toujours fastidieux mais au moins c'est lisible.
la source
command1 &> /dev/null || echo "command1 borked it"
command1 || (echo command1 borked it ; exit)
Une alternative consiste simplement à joindre les commandes avec
&&
afin que la première à échouer empêche le reste de s'exécuter:Ce n'est pas la syntaxe que vous avez demandée dans la question, mais c'est un modèle courant pour le cas d'utilisation que vous décrivez. En général, les commandes doivent être responsables des échecs d'impression afin que vous n'ayez pas à le faire manuellement (peut-être avec un
-q
indicateur pour réduire les erreurs lorsque vous ne les voulez pas). Si vous avez la possibilité de modifier ces commandes, je les éditerais pour crier en cas d'échec, plutôt que de les envelopper dans autre chose qui le fait.Notez également que vous n'avez pas besoin de faire:
Vous pouvez simplement dire:
Et quand vous avez besoin de vérifier les codes de retour utilisent un contexte arithmétique au lieu de
[ ... -ne
:la source
Au lieu de créer des fonctions d'exécution ou d'utiliser
set -e
, utilisez untrap
:Le piège a même accès au numéro de ligne et à la ligne de commande de la commande qui l'a déclenché. Les variables sont
$BASH_LINENO
et$BASH_COMMAND
.la source
trap - ERR
pour désactiver le piège à la fin du "bloc".Personnellement, je préfère de loin utiliser une approche légère, comme on le voit ici ;
Exemple d'utilisation:
la source
la source
$*
, cela échouera si des arguments contiennent des espaces; utiliser à la"$@"
place. (Bien que $ * soit correct dans laecho
commande.)J'ai développé une implémentation try & catch presque sans faille dans bash, qui vous permet d'écrire du code comme:
Vous pouvez même imbriquer les blocs try-catch à l'intérieur d'eux-mêmes!
Le code fait partie de ma plateforme / framework bash . Il étend encore l'idée de try & catch avec des choses comme la gestion des erreurs avec backtrace et exceptions (ainsi que d'autres fonctionnalités intéressantes).
Voici le code qui est juste responsable de try & catch:
N'hésitez pas à utiliser, à fourche et à contribuer - c'est sur GitHub .
la source
Désolé de ne pas pouvoir commenter la première réponse Mais vous devez utiliser une nouvelle instance pour exécuter la commande: cmd_output = $ ($ @)
la source
Pour les utilisateurs de coquille de poisson qui tombent sur ce fil.
Soit
foo
une fonction qui ne "renvoie" pas (écho) une valeur, mais elle définit le code de sortie comme d'habitude.Pour éviter de vérifier
$status
après avoir appelé la fonction, vous pouvez faire:Et s'il est trop long pour tenir sur une seule ligne:
la source
Lorsque j'utilise,
ssh
je dois distinguer les problèmes causés par des problèmes de connexion et les codes d'erreur de la commande à distance en modeerrexit
(set -e
). J'utilise la fonction suivante:la source
Vous pouvez utiliser la solution géniale de @ john-kugelman trouvée ci-dessus sur les systèmes non RedHat en commentant cette ligne dans son code:
Ensuite, collez le code ci-dessous à la fin. Divulgation complète: il s'agit simplement d'un copier-coller direct des bits pertinents du fichier mentionné ci-dessus provenant de Centos 7.
Testé sur MacOS et Ubuntu 18.04.
la source
Vérifier l'état de manière fonctionnelle
Usage:
Production
la source