Comment faire quelque chose sous condition si une commande réussit ou échoue

283

Comment puis-je faire quelque chose comme ça dans bash?

if "`command` returns any error";
then
    echo "Returned an error"
else
    echo "Proceed..."
fi
Michael Mrozek
la source

Réponses:

359

Comment faire quelque chose sous condition si une commande réussit ou échoue

C'est exactement ce que dit la ifdéclaration de bash :

if command ; then
    echo "Command succeeded"
else
    echo "Command failed"
fi

Ajout d'informations à partir de commentaires: vous n'avez pas besoin d'utiliser la syntaxe [... ]dans ce cas. [est lui-même une commande, presque équivalente à test. C'est probablement la commande la plus courante à utiliser dans un if, ce qui peut laisser supposer que cela fait partie de la syntaxe du shell. Mais si vous voulez vérifier si une commande a réussi ou non, utilisez-la directement avec if, comme indiqué ci-dessus.

Éditer: Cité la question en haut pour plus de clarté (cette réponse n'apparaît pas en haut de la page).

Keith Thompson
la source
11
Notez que le point-virgule est important.
Thorbjørn Ravn Andersen
21
Ou vous pouvez simplement mettre thensur une ligne séparée.
l0h07
36
@ Joe: Je pense que tu veux dire if ! command ; then ... ; fi. [est lui-même une commande, et ce n'est pas nécessaire dans ce cas.
Keith Thompson
20
@ Joe: Mon chemin a aussi le mérite d'être correct. if [ ! command ]n'exécute pas command; il traite commandcomme une chaîne et la traite comme étant vraie car sa longueur est non nulle. [est un synonyme de testcommande
Keith Thompson
5
Oops. Vous avez raison.
Joe
133

Pour les petites choses que vous voulez voir si une commande shell fonctionne, vous pouvez utiliser la &&construction:

rm -rf somedir && trace_output "Removed the directory"

De même pour les petites choses que vous souhaitez voir se produire lorsqu'une commande shell échoue, vous pouvez utiliser ||:

rm -rf somedir || exit_on_error "Failed to remove the directory"

Ou les deux

rm -rf somedir && trace_output "Removed the directory" || exit_on_error "Failed to remove the directory"

Il est probablement peu judicieux de faire beaucoup avec ces constructions, mais elles peuvent parfois rendre le flux de contrôle beaucoup plus clair.

Bruce Ediger
la source
3
Ils sont plus courts et (du moins dans certains coquillages) plus rapides. Je frémis en me souvenant d'un script d'installation monstre Ultrix écrit avec juste ces constructions conditionnelles que j'avais déjà essayé de déchiffrer ...
vonbrand
101

Vérifiez la valeur de $?, qui contient le résultat de l'exécution de la commande / fonction la plus récente:

#!/bin/bash

echo "this will work"
RESULT=$?
if [ $RESULT -eq 0 ]; then
  echo success
else
  echo failed
fi

if [ $RESULT == 0 ]; then
  echo success 2
else
  echo failed 2
fi

la source
11
Bien que techniquement correct (et donc ne garantissant pas un vote négatif), il ne fait pas usage de l' ifidiome de Bash . Je préfère la réponse de Keith Thompson.
janmoesen
16
Cet idiome présente des avantages - il préserve la valeur de retour. Au total, je trouve que celui-ci est plus puissant, bien que plus verbeux. c'est aussi plus facile à lire.
Taxilian
2
Qu'est-ce que "Bash's if idiom"?
Nowaker
3
@Nowaker Le fait que le seul but de ifest de faire cela. Les conditions de contrôle de flux dans Bash examinent toutes $?les coulisses; c'est ce qu'ils font. L'examen explicite de sa valeur devrait être inutile dans la grande majorité des cas et constitue généralement un anti-modèle pour débutant.
tripleee
1
@lunakid Si vous ne vous souciez pas du code de sortie, if ! cmdc'est bien. Sinon, un arrangement commun consiste à utiliser une elseclause. Vous ne pouvez pas avoir un vide thenmais vous voyez parfois then : nothing; elseoù le :no-op est important. truetravaillerait là aussi.
triplee
47

Cela a fonctionné pour moi:

command && echo "OK" || echo "NOK"

si commandréussit, alors echo "OK"est exécuté, et puisque cela réussit, l'exécution s'arrête là. Sinon, &&est ignoré et echo "NOK"est exécuté.

Rumm allemand
la source
5
Si vous voulez faire quelque chose en cas d'échec et conserver le code de sortie (à afficher dans une invite de commande ou à tester dans un script), vous pouvez le faire: command && echo "OK" || c=$?; echo "NOK"; $(exit $c)
Sam Hasler
2
@ Sam-Hasler: ne devrait-il pas en être ainsi command && echo "OK" || (c=$?; echo "NOK"; (exit $c))?
jrw32982
9
De plus, si la echo "OK"pièce pouvait elle-même échouer, alors c'est mieux:command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))
jrw32982
@ jrw32982, Nice, j'ai utilisé l'ancienne construction, mais pas la dernière.
Sam Hasler
La vraie réponse est dans les commentaires, merci à tous!
Joshua Pinter
6

Il convient de noter que if...then...fiet &&/ ||type d’approche concerne l’état de sortie renvoyé par la commande à tester (0 en cas de succès); Cependant, certaines commandes ne renvoient pas de statut de sortie non nul si la commande échoue ou ne peut pas traiter les entrées. Cela signifie que les approches habituelles ifet &&/ ||ne fonctionneront pas pour ces commandes particulières.

Par exemple, sous Linux, GNU filese ferme toujours à 0 s'il a reçu un fichier non existant en argument et findne peut pas localiser le fichier spécifié par l'utilisateur.

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0

Dans de tels cas, une solution potentielle consiste à lire stderr/ les stdinmessages, par exemple ceux renvoyés par filecommande, ou à analyser le résultat de la commande comme dans find. À cette fin, une casedéclaration pourrait être utilisée.

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found
Sergiy Kolodyazhnyy
la source
2

Le plus susceptible d’être entaché d’erreur était:

  • Tout d'abord, obtenez la valeur. Supposons que vous fassiez quelque chose comme:

RR=$?

Maintenant, non seulement pour cette situation, mais pour d’autres que vous pourriez rencontrer, considérez:

variable définie:

$ AA=1 ; if (( "10#0${AA}" == 1 )) ; then echo yes ; else echo no ; fi

Réponse: oui

$ AA=1 ; if (( "10#0${AA}" != 1 )) ; then echo yes ; else echo no ; fi

Réponse: non

variable indéfinie:

$ AA=1 ; if (( "10#0${BB}" == 1 )) ; then echo yes ; else echo no ; fi

Réponse: non

$ AA=1 ; if (( "10#0${BB}" != 1 )) ; then echo yes ; else echo no ; fi

Réponse: oui

$ AA=1 ; if (( "10#0${BB}" == 0 )) ; then echo yes ; else echo no ; fi

Réponse: oui

Cela empêche toutes sortes d'erreurs.

Vous êtes probablement au courant de toute la syntaxe, mais voici quelques conseils:

  • Utilisez des citations. Évitez "blank"d'être nothing.
  • La nouvelle notation moderne pour les variables est ${variable}.
  • L'ajout d'un zéro concaténé avant votre numéro évite également "pas de numéro du tout".
  • Mais attendez, ajouter un zéro rend le nombre base-8. Vous obtiendrez une erreur comme:
    • value too great for base (error token is "08")pour les nombres ci-dessus 7. C'est quand 10#entre en jeu:
    • 10#oblige le nombre à être base-10.
Dr Beco
la source
1

Tu peux le faire:

if ($( ping 4.4.4.4 -c1 > /dev/null )) ; then
  echo "ping response succsess!!!"
fi
Daniel
la source
9
Cela fonctionne mais est compliqué. Vous exécutez ping dans un sous-shell d'un sous-shell, la sortie de celle-ci pingest capturée afin de l'exécuter en tant que commande. Mais parce que la sortie est redirigée vers / dev / null ce sera toujours la chaîne vide. Donc, vous n’exécutez rien dans un sous-shell, ce qui signifie que le statut de sortie précédent (du sous-shell de substitution de commande, c’est-à-dire de ping) sera conservé. De toute évidence, la bonne façon est if ping ...; thenici.
Stéphane Chazelas
1

Comme noté ailleurs dans ce fil, la question initiale se répond fondamentalement à elle-même. Voici une illustration montrant que des ifconditions peuvent également être imbriquées.

Cet exemple permet ifde vérifier si un fichier existe et s'il s'agit d'un fichier normal. Si ces conditions sont vraies, vérifiez si sa taille est supérieure à 0.

#!/bin/bash

echo "Which error log are you checking today? "
read answer

if [ -f /opt/logs/$answer*.errors ]
    then
        if [ -s /opt/logs/$answer*.errors ]
            then
                echo "Content is present in the $answer error log file."
            else
                echo "No errors are present in the $answer error log file."
        fi
    else
        echo "$answer does not have an error log at this time."
fi
quartzinquartz
la source
2
C'est ce que fait votre réponse, mais votre réponse ne répond pas à la question.
Jeff Schaller
@ JeffSchaller, merci pour votre note. J'ai édité mon post pour inclure une référence à la question.
quartzinquartz
1
#!/bin/bash

if command-1 ; then
   echo "command-1 succeeded and now running from this block command-2"
   command-2
else
   echo "command-1 failed and now running from this block command-3"
   command-3
fi
Nick Vladiceanu
la source
3
Cela ressemble un peu à la réponse acceptée existante ; Veuillez attribuer le travail que vous réutilisez ou ajouter vos propres efforts dans vos réponses.
Jeff Schaller
-3

Ceci pourrait être fait simplement de cette manière, car il $?vous donne le statut de la dernière commande exécutée.

Donc ça pourrait être

#!/bin/sh

... some command ...

if [ $? == 0 ] ; then
  echo '<the output message you want to display>'
else 
  echo '<failure message>'
fi
Vipin Mishra
la source
1
Downvote: Ceci paraphrase simplement une réponse antérieure qui a à juste titre reçu les critiques pour être unidiomatique.
tripleee