J'essaie de comprendre comment fonctionne la priorité de l'opérateur logique dans bash. Par exemple, je me serais attendu à ce que la commande suivante ne fasse écho à rien.
true || echo aaa && echo bbb
Cependant, contrairement à mes attentes, bbb
est imprimé.
Quelqu'un peut-il expliquer, comment puis-je donner un sens à _ &&
et ||
opérateurs en bash?
Dans de nombreux langages informatiques, les opérateurs ayant la même priorité sont associatifs à gauche . Autrement dit, en l'absence de structures de regroupement, les opérations les plus à gauche sont exécutées en premier. Bash est sans exception à cette règle.
Ceci est important car, dans Bash, &&
et ||
ont la même priorité.
Donc, ce qui se passe dans votre exemple, c'est que l'opération la plus à gauche (||
) s'effectue d'abord:
true || echo aaa
Puisque true
est évidemment vrai, le ||
court-circuits de l'opérateur et la déclaration entière est considérée comme vraie sans qu'il soit nécessaire d'évaluer echo aaa
comme vous vous en doutez. Reste maintenant à faire l'opération la plus à droite:
(...) && echo bbb
Depuis la première opération évaluée à vrai (c.-à-d. Avait un état de sortie 0), c'est comme si vous exécutiez
true && echo bbb
alors le &&
ne court-circuite pas, c'est pourquoi vous voyez bbb
en écho.
Vous obtiendriez le même comportement avec
false && echo aaa || echo bbb
Notes basées sur les commentaires
[[...]]
ou ((...))
ou utilisez le -o
et -a
opérateurs comme arguments des test
ou [
commandes. Dans de tels cas, ET (&&
ou -a
) a priorité sur OR (||
ou -o
). Merci au commentaire de Stéphane Chazelas d'avoir clarifié ce point.Il semble que en C et langages de type C &&
a une priorité plus élevée que ||
c'est probablement pourquoi vous vous attendiez à ce que votre construction d'origine se comporte comme
true || (echo aaa && echo bbb).
Ce n'est pas le cas avec Bash, cependant, dans lequel les deux opérateurs ont la même priorité, c'est pourquoi Bash analyse votre expression à l'aide de la règle d'associativité gauche. Merci au commentaire de Kevin d'avoir soulevé cette question.
Il peut également y avoir des cas où les expressions tous les 3 sont évaluées. Si la première commande renvoie un état de sortie différent de zéro, le ||
ne court-circuite pas et continue d'exécuter la deuxième commande. Si la deuxième commande retourne avec un état de sortie nul, alors le &&
ne court-circuite pas aussi bien et la troisième commande sera exécutée. Merci au commentaire d'Ignacio Vazquez-Abrams d'avoir soulevé cette question.
Si vous souhaitez que plusieurs éléments dépendent de votre état, regroupez-les:
true || { echo aaa && echo bbb; }
Cela n'imprime rien,
true && { echo aaa && echo bbb; }
imprime les deux chaînes.
La raison pour laquelle cela se produit est beaucoup plus simple que Joseph ne le prétend. Rappelez-vous ce que Bash fait avec ||
et &&
. Tout dépend du statut de retour de la commande précédente. Une façon littérale de regarder votre commande brute est la suivante:
( true || echo aaa ) && echo bbb
La première commande (true || echo aaa
) se termine avec 0
.
$ true || echo aaa; echo $?
0
$ true && echo aaa; echo $?
aaa
0
$ false && echo aaa; echo $?
1
$ false || echo aaa; echo $?
aaa
0
Le &&
et ||
les opérateurs sont ( pas remplacements exacts en ligne pour if-then-else. Bien que s'ils sont utilisés avec soin, ils peuvent accomplir à peu près la même chose.
Un seul test est simple et sans ambiguïté ...
[[ A == A ]] && echo TRUE # TRUE
[[ A == B ]] && echo TRUE #
[[ A == A ]] || echo FALSE #
[[ A == B ]] || echo FALSE # FALSE
Cependant, tenter d'ajouter plusieurs tests peut donner des résultats inattendus ...
[[ A == A ]] && echo TRUE || echo FALSE # TRUE (as expected)
[[ A == B ]] && echo TRUE || echo FALSE # FALSE (as expected)
[[ A == A ]] || echo FALSE && echo TRUE # TRUE (as expected)
[[ A == B ]] || echo FALSE && echo TRUE # FALSE TRUE (huh?)
Pourquoi FALSE et TRUE sont-ils répercutés?
Ce qui se passe ici, c'est que nous n'avons pas réalisé que &&
et ||
sont des opérateurs surchargés qui agissent différemment à l'intérieur des crochets de test conditionnels [[ ]]
que dans les listes ET et OR (exécution conditionnelle) que nous avons ici.
Depuis la page de manuel bash (éditée) ...
Listes
Une liste est une séquence d'un ou plusieurs pipelines séparés par l'un des opérateurs;, &, &&, ou ││, et éventuellement terminé par l'un des;, &, ou. Parmi ces opérateurs de liste, && et ││ ont la même priorité, suivis de; et &, qui ont la même priorité.
Une séquence d'un ou plusieurs sauts de ligne peut apparaître dans une liste au lieu d'un point-virgule pour délimiter les commandes.
Si une commande est interrompue par l'opérateur de contrôle &, le shell exécute la commande en arrière-plan dans un sous-shell. Le shell n'attend pas la fin de la commande et l'état de retour est 0. Commandes séparées par un; sont exécutés séquentiellement; le shell attend que chaque commande se termine à son tour. Le statut de retour est le statut de sortie de la dernière commande exécutée.
ET et OR les listes sont des séquences d'un ou plusieurs pipelines séparés respectivement par les opérateurs de contrôle && et respectively. ET et OR les listes sont exécutées avec l'associativité gauche) .
Une liste ET a la forme ...
command1 && command2
La commande2 est exécutée si et seulement si la commande1 renvoie un état de sortie nul.Une liste OR a la forme ...
command1 ││ command2
Command2 est exécutée si et seulement si command1 renvoie un état de sortie différent de zéro.L'état de retour de AND et OR lists est l'état de sortie de la dernière commande exécutée dans la liste.
Revenons à notre dernier exemple ...
[[ A == B ]] || echo FALSE && echo TRUE
[[ A == B ]] is false
|| Does NOT mean OR! It means...
'execute next command if last command return code(rc) was false'
echo FALSE The 'echo' command rc is always true
(i.e. it successfully echoed the Word "FALSE")
&& Execute next command if last command rc was true
echo TRUE Since the 'echo FALSE' rc was true, then echo "TRUE"
D'accord. Si c'est exact, pourquoi l'avant-dernier exemple fait-il écho à quelque chose?
[[ A == A ]] || echo FALSE && echo TRUE
[[ A == A ]] is true
|| execute next command if last command rc was false.
echo FALSE Since last rc was true, shouldn't it have stopped before this?
Nope. Instead, it skips the 'echo FALSE', does not even try to
execute it, and continues looking for a `&&` clause.
&& ... which it finds here
echo TRUE ... so, since `[[ A == A ]]` is true, then it echos "TRUE"
Le risque d'erreurs logiques lors de l'utilisation de plusieurs &&
ou ||
dans une liste de commandes est assez élevé.
Recommandations
Un seul &&
ou ||
dans une liste de commandes fonctionne comme prévu et est donc assez sûr. S'il s'agit d'une situation où vous n'avez pas besoin d'une clause else, quelque chose comme ce qui suit peut être plus clair à suivre (les accolades sont nécessaires pour regrouper les 2 dernières commandes) ...
[[ $1 == --help ]] && { echo "$HELP"; exit; }
Plusieurs &&
et ||
opérateurs, où chaque commande, à l'exception de la dernière, est un test (c'est-à-dire entre crochets [[ ]]
), sont généralement aussi sûrs que tous, mais le dernier opérateur se comporte comme prévu. Le dernier opérateur agit plus comme une clause then
ou else
.
J'ai également été confus par cela, mais voici comment je pense à la façon dont Bash lit votre déclaration (car elle lit les symboles de gauche à droite):
true
. Cela devra être évalué une fois la fin de la commande atteinte. À ce stade, je ne sais pas s'il contient des arguments. Stocker la commande dans le tampon d'exécution.||
. La commande précédente est maintenant terminée, alors évaluez-la. Commande (tampon) en cours d'exécution: true
. Résultat de l'évaluation: 0 (c'est-à-dire succès). Enregistrer le résultat 0 dans le registre "dernière évaluation". Considérez maintenant le symbole ||
lui-même. Cela dépend du résultat de la dernière évaluation étant non nul. Registre "dernière évaluation" vérifié et trouvé 0. Puisque 0 n'est pas différent de zéro, la commande suivante n'a pas besoin d'être évaluée.echo
. Peut ignorer ce symbole, car la commande suivante n'a pas besoin d'être évaluée.aaa
. C'est un argument pour commander echo
(3), mais comme echo
(3) n'a pas besoin d'être évalué, il peut être ignoré.&&
. Cela dépend du résultat de la dernière évaluation étant nul. Registre "dernière évaluation" vérifié et trouvé 0. Puisque 0 est zéro, la commande suivante doit être évaluée.echo
. Cette commande doit être évaluée une fois la fin de la commande atteinte, car la commande suivante a dû être évaluée. Stocker la commande dans le tampon d'exécution.bbb
. C'est un argument pour commander echo
(6). Puisque echo
devait être évalué, ajoutez bbb
au tampon d'exécution.echo bbb
. Résultat de l'évaluation: 0 (c'est-à-dire succès). Enregistrer le résultat 0 dans le registre "dernière évaluation".Et bien sûr, la dernière étape fait en sorte que bbb
soit répercuté sur la console.