Quel est l'équivalent de && lors de l'écriture d'un script bash?

31

Je m'excuse à l'avance s'il s'agit d'une question en double. J'ai fait un effort pour rechercher / vérifier avant de demander ici.

Je suis à l'aise avec l'écriture de lignes simples comme ceci:

foocommand && foocommand2 && foocommand3

L'idée étant que je souhaite que les commandes suivantes s'exécutent uniquement si la précédente a "réussi".

J'écris un script un peu long et ce one-liner n'est pas réalisable car il ressemble à un énorme bloc de code déroutant pour tout le monde.

Je veux espacer les commandes et écrire des commentaires entre elles dans le script. Comment puis-je faire cela et avoir encore l'équivalent de && là-dedans?

Mike B
la source
4
Il faut le répéter: &&ne signifie pas que la commande suivante fonctionnera si le précédent un a réussi. Cela signifie que la commande s'exécutera si le résultat collectif de toutes les commandes précédentes dans la liste de commandes est réussi. Vous le savez peut-être, mais les futurs lecteurs peuvent se méprendre.
kojiro
1
Également, juste comme une note, ce comportement que vous décrivez provient ||et &&(par opposition à |ou &) est appelé court-circuit. Le comportement utilisé avec ces derniers opérateurs est appelé évaluation avide.
AndyPerfect
7
@kojiro: Je ne vois pas la distinction. a && b && cne s'exécutera que bsi aréussit, donc "il ne s'exécute que csi bréussit" et "il ne s'exécute que csi aet les bdeux réussissent" sont des instructions équivalentes: bne peut réussir que s'il aréussit.
ruakh
1
@ruakh a || b && cest plus illustratif.
kojiro
8
@ruakh non, true || false && echo hisortira hi. La spécification se lit comme suit: Les opérateurs "&&" et "||" doivent avoir la même priorité et doivent être évalués avec l'associativité gauche.
kojiro

Réponses:

23

Vous pouvez le faire comme ceci:

#!/bin/sh
ls -lh &&
    # This is a comment
    echo 'Wicked, it works!'

J'espère avoir bien compris ce que vous avez demandé.

Schaiba
la source
Merci. C'est le plus proche de ce que j'espérais faire. Et il est très facile de reconvertir en un revêtement si nécessaire.
Mike B
Mike, puisque vous êtes préoccupé par la propreté du code, une convention courante consiste à mettre en retrait les sections 2-N de la chaîne sous la première.
jblaine
@jblaine Je ne suis pas sûr de comprendre ce que tu veux dire. Pouvez-vous s'il vous plaît développer?
Mike B
Mike, StackExchange ne me laissera pas formater ma réponse correctement, voici donc tout ce que je voulait dire: tiret
jblaine
@jblaine Bon point, modifié pour mettre en retrait les lignes.
Volker Siegel
38

Vous pouvez changer la ligne de shebang en

#!/bin/bash -e

Après toute erreur, le script s'arrête.

choroba
la source
1
Intéressant. Je garderai ça à l'esprit. Merci.
Mike B
m'évite
@Silverrocker C'est POSIX aussi (sauf pour la bashpartie bien sûr). Le shell POSIX prend un tas d'options applicables à set. Ou vice versa? setest un moyen de définir les options de ligne de commande du shell dans le script.
Kaz
7
Malheureusement, il existe un certain nombre d'utilitaires standard qui ne respectent pas les conventions habituelles sur l'état de sortie, et -epeuvent donc se comporter mal. Par exemple, vous ne voulez probablement pas que votre script se termine à chaque fois que vous diffdeux fichiers non identiques. Vous pouvez, bien sûr, contourner cela en ajoutant || true(ou peut-être || [ $? = 1 ]) à de telles commandes, mais l'OP mentionne un "tout le monde" qui devra lire ce script, et a dit "tout le monde" ne sera probablement pas habitué à devoir faire face -e.
ruakh
4
J'avais l'habitude de mettre le -esur le coup, jusqu'à ce qu'un administrateur de ma société continue de lancer mes scripts avec bash myscript.sh, donc il a ignoré le -e. Maintenant, tous mes scripts ont un set -edans la deuxième ligne.
Carlos Campderrós
19

Si vous n'aimez pas l' set -eidée, vous pouvez peut-être inverser la logique.

foocommand || exit 1
foocommand2 || exit 2
foocommand3 || exit 3

Plus utilement, remplacez exitpar quelque chose pour imprimer un message d'erreur utile, puis quittez. Dans une fonction, bien sûr, vous voulez returnau lieu de exit.

tripleee
la source
11
Ou foocommand || exitpour quitter avec foocommandle statut de sortie de si différent de zéro.
Stéphane Chazelas
Comment cela marche-t-il? Est-ce comme en C - si l'argument de gauche est évalué à TRUE, les autres arguments ne sont pas vérifiés?
Vorac
Oui c'est vrai. C'est un idiome courant en Lisp (et les langages fonctionnels en général, je suppose) et en Perl.
tripleee
9

Vous pouvez utiliser des if else fiblocs à la place.

if foocommand; then

  # some comments

  if foocommand2; then

    # more comments

    foocommand3
  fi
fi

C'est un peu plus lisible.

Alternativement, vous pouvez simplement utiliser \pour briser votre grand liner en plusieurs lignes

foocommand && \
  # some comment
  foocommand2 && \
    # more comment
    foocommand3

Mais bien sûr, cela peut être déroutant pour l'œil non averti.

Martín Canaval
la source
4
Je ne pense pas que ce \soit nécessaire là-bas.
Samuel Edwin Ward
Vous avez raison. Force d'habitude.
Martín Canaval
5

Vous pouvez envisager d'utiliser des ifinstructions imbriquées . Une autre option consiste à utiliser des bouclés pour regrouper comme{ true && echo hi; } || echo huh

Mise à jour 1:

Voici un exemple sans nouvelles lignes / commentaires:

{ { false && echo "inner true"; } && { echo "inner true" && true; } || { echo "inner false" && false; } || echo "outter false"; }
livingstaccato
la source
@Stephane Merci d'avoir ajouté un point-virgule. J'utilise mon téléphone pour répondre, je n'ai donc pas vérifié mes réponses. :)
livingstaccato
Idée intéressante. Oui, j'avais pensé à des déclarations imbriquées, mais si j'enchaîne beaucoup de choses ensemble, cela pourrait devenir compliqué.
Mike B
4

Divisez chaque séquence de commandes en fonctions:

big_block_1() {
  # ...
}
big_block_2() {
  # ...
}
big_block_3() {
  # ...
}

big_block_1 && big_block_2 && big_block_3
Fuad Saud
la source
0

J'aime toujours écrire une fonction "échec" qui me permet de spécifier la chose que j'allais faire lorsque l'échec s'est produit, puis d'utiliser le ||modèle. Comme le suivant:

function fail() {
  echo "Failure: ${@}"
  exit 1
}

foocommand || fail "couldn't foo"
Dave
la source