Bash: A && B || C is not if-then-else

A && B || C is not if-then-else. C may run when A is true

Je comprends bien le message, mais je ne suis pas sur de comprendre pourquoi (quand, Comment) C peut être exécuté quand A est vrai.

Je suppose que cette mise en garde peut signifier que C teste le résultat de B et non de A comme le ferait if-the-else.

Lorsque B est echo " $Foo", même si Foo n’existe pas, la valeur de retour reste vraie.

Mais je suppose que si B est une commande qui se termine par faux (valeur > 0), dans ce cas C s’exécuterait?

Jusqu’ici, j’ai toujours eu la chance de ne pas rencontrer ce problème. Sans doute car cette forme n’est utilisée que pour une commande simple comme «echo».

Est-ce bien l’explication de cette mise en garde (de shellcheck) ?

Il s’agit d’une ambiguïté non POSIX a impérativement éviter d’un shell à l’autre, car peut vouloir traduire deux conditions différentes:

Soit:
Si A et B sont vraies, ou bien seulement si C est vrai, alors …
{ A && B ;} || C est correct et portable.

ou:
Si A est vrai, et si B ou C est vraie , alors…
A && { B || C ;} est correct et portable.

Imagine A vrai et B faux.
Si c’est parenthésé comme (A && B) || C , tu auras A executé puis comme il est vrai B aussi et donc comme B faux, (A && B) faux et donc C est exécuté
Si c’est parenthésé comme A && (B || C) tu as le même enchainement:
A est executé, étant vrai B || C est executé, donc B et comme B est faux C

En fait en bash si tu fais A && B || C il est impératif que B (pour moi C aussi) ne renvoit pas de code d’erreur.

1 J'aime

C’est bien ce que j’imaginais.
Les exemples parenthésés sont intéressants, et donc utilisables.

Merci à tous les deux.

Attention à la confusion courante entre l’usage de { ou ( : ce n’est pas un effet de style.
« { » teste la condition dans le process en cours (donc plus rapide).
« ( » ouvre un sous-process, qui isole le test: attention au passage de variables d’un process à l’autre (piège courant).

Ah oui, en effet j’avais mal vu (mal compris donc!)

Pour illustrer la confusion courante entre « ( » et « { »

A=0 ; { A=1 ;} ; echo $A
1

A=0 ; ( A=1 )  ; echo $A
0

Pour éviter les surprises et bugs obscurs, un test ne doit être isolé dans un sous-process que si justifié seulement.

J’ai vérifié et, apparemment, c’est POSIX.

Et non, ce n’est pas une ambiguïté.

D’après la page de man bash , l’associativité à gauche fait que
A && B || C
équivaut à
( A && B ) || C

Prenons l’exemple de l’explication fournie par shellcheck.net , SC2015 :
[[ $dryrun ]] && echo "Would delete file" || rm file

Si un petit malin redirige la sortie standard vers /dev/full , la commande echo renverrait une erreur et le rm serait exécuté.


AnonymousCoward

Le problème est dans l’apparence.
Bash n’est pas POSIX. Dash est POSIX.
Est-ce que zsh aura aussi le même comportement selon les versions ?
Absolument aucune garantie.
Maintenant, il n’est pas interdit d’utiliser des formulations ambigües, particulièrement si’il n’y a que 3 conditions à tester, mais c’est jouer au poker et complexifier la recherche de bugs.
Ce n’est pas par hasard que le problème est signalé, car parfaitement identifié.
Prendre de mauvaises habitudes n’est pas une bonne habitude, mais chacun fait fait fait ce qui lui plait plait plait.

J’ai testé avec l’option --posix de bash ainsi qu’avec dash et l’expression A && B || C est valide dans les deux cas, a le même fonctionnement.

Cela n’a rien d’ambigu.

Mais oui, c’est une bonne habitude que d’utiliser systématiquement if-then-else plutôt que && et || .


AnonymousCoward

Une option ‹ –posix › que ce soit de bash ou autre commande non posix n’est pas une garantie de ‹ posixité › absolue, sachant qu’il a POSIX et POSIX selon la version. Problème aussi rencontré avec awk / gawk.

On peut rajouter qu’on ne trouvera jamais ce genre d’ambiguïté ’A && B || C ’ dans aucun script POSIX dans un paquet Debian par exemple.
Ceux qui écrivent ces scripts connaissent bien ce problème.
C’est en pratiquant des tests plus complexes, en testant leur portabilité qu’on est sensibilisé à ce genre de problématique.

Exemple
•••false is not true && écho bien sûr || écho Quel et le con qui a codé $(command -v false) ?

Désolé pour les fautes de frappe, je galère à taper sur un vieil iPhone.