web-dev-qa-db-fra.com

Comparaison d'entiers: expression arithmétique ou expression conditionnelle

Dans Bash, deux entiers peuvent être comparés à l'aide d'une expression conditionnelle

arg1 OP arg2

OP est l'un des -eq, -ne, -lt, -le, -gt, ou -ge. Ces opérateurs binaires arithmétiques renvoient true si arg1 est égal à, non égal à, inférieur à, inférieur ou égal à, supérieur ou supérieur ou égal à arg2, respectivement. Arg1 et arg2 peuvent être des entiers positifs ou négatifs.

ou expression arithmétique:

<= >= < > Comparaison

== != égalité et inégalité

Pourquoi avons-nous deux façons différentes de comparer deux entiers? Quand utiliser quoi?

Par exemple, [[ 3 -lt 2 ]] utilise une expression conditionnelle et (( 3 < 2 )) utilise une expression arithmétique. Les deux renvoient 0 lorsque la comparaison est vraie

Lors de la comparaison de deux nombres entiers, ces deux méthodes peuvent-elles toujours être utilisées de manière interchangeable? Si oui, pourquoi Bash a-t-il deux méthodes plutôt qu'une?

24
Tim

Oui, nous avons deux façons différentes de comparer deux entiers.

Il semble que ces faits ne soient pas largement acceptés dans ce forum:

  1. À l'intérieur de l'idiome _[ ]_ les opérateurs de comparaison arithmétique sont _-eq_, _-ne_, _-lt_, _-le_, _-gt_ et _-ge_.

    Comme ils sont également à l'intérieur d'une commande de test et à l'intérieur d'un _[[ ]]_.

    Oui à l'intérieur de ces idiomes, _=_, _<_, etc. sont des opérateurs de chaîne.

  2. À l'intérieur de l'idiome _(( ))_ les opérateurs de comparaison arithmétique sont _==_, _!=_, _<_, _<=_, _>_, et _>=_ .

    Non, ce n'est pas une "expansion arithmétique" (qui commence par un _$_) comme $(( )). Il est défini comme une "commande composée" dans man bash.

    Oui, il suit les mêmes règles (internes) de "l'expansion arithmétique" mais n'a pas de sortie, seulement une valeur de sortie. Il pourrait être utilisé comme ceci:

_if (( 2 > 1 )); then ...
_

Pourquoi avons-nous deux façons différentes de comparer deux entiers?

Je suppose que ce dernier _(( ))_ a été développé comme un moyen plus simple d'effectuer des tests arithmétiques. C'est presque la même chose que la $(( )) mais n'a juste aucune sortie.

Pourquoi deux? Eh bien, la même chose que la raison pour laquelle nous avons deux printf (externe et intégré) ou quatre tests (externe test, intégré test, _[_ et _[[_ ). C'est ainsi que les coquilles se développent, améliorant une zone en un an, en améliorant une autre l'année suivante.

Quand utiliser quoi?

C'est une question très difficile car il ne devrait pas y avoir de différence effective. Bien sûr, il existe des différences dans la façon dont un _[ ]_ fonctionne et un _(( ))_ fonctionne en interne, mais: quelle est la meilleure solution pour comparer deux entiers? N'importe qui!.

Lors de la comparaison de deux nombres entiers, ces deux méthodes peuvent-elles toujours être utilisées de manière interchangeable?

Pour deux chiffres, je suis obligé de dire oui.
Mais pour les variables, les extensions, les opérations mathématiques, il peut y avoir des différences clés qui devraient favoriser l'une ou l'autre. Je ne peux pas dire que les deux sont absolument égaux. D'une part, le _(( ))_ pourrait effectuer plusieurs opérations mathématiques en séquence:

_if (( a=1, b=2, c=a+b*b )); then echo "$c"; fi
_

Si oui, pourquoi Bash a-t-il deux méthodes plutôt qu'une?

Si les deux sont utiles, pourquoi pas?.

32
user79743

Historiquement, la commande test a existé en premier (au moins aussi loin que nix Seventh Edition en 1979). Il a utilisé les opérateurs = et != pour comparer les chaînes et -eq, -ne, -lt, etc. pour comparer les nombres. Par exemple, test 0 = 00 est faux, mais test 0 -eq 00 est vrai. Je ne sais pas pourquoi cette syntaxe a été choisie, mais c'était peut-être pour éviter d'utiliser < et >, que Shell aurait analysé en tant qu'opérateurs de redirection. La commande test a obtenu une autre syntaxe quelques années plus tard: [ … ] est équivalent à test ….

Le [[ … ]] syntaxe conditionnelle, à l'intérieur de laquelle < et > peut être utilisé comme opérateur sans guillemet, a été ajouté plus tard, dans ksh. Il a conservé la compatibilité descendante avec [ … ], il a donc utilisé les mêmes opérateurs, mais a ajouté < et > pour comparer des chaînes (par exemple, [[ 9 > 10 ]] mais [[ 9 -lt 10 ]]). Pour plus d'informations, voir en utilisant un support simple ou double - bash

Les expressions arithmétiques sont également arrivées plus tard que la commande test, dans le Korn Shell , à un moment donné dans les années 1980. Ils ont suivi la syntaxe du langage C, qui était très populaire dans les cercles Unix. Ils ont donc utilisé les opérateurs de C: == pour l'égalité, <= pour inférieur ou égal, etc.

Unix Seventh Edition n'avait pas d'expressions arithmétiques, mais il avait la commande expr , qui implémentait également une syntaxe de type C pour les opérations entières, y compris ses opérateurs de comparaison. Dans un script Shell, les caractères < et > devait être cité pour les protéger du shell, par exemple if expr 1 \< 2; … est équivalent à if test 1 -lt 2; …. L'ajout d'expressions arithmétiques au Shell a rendu la plupart des utilisations de expr obsolètes, donc ce n'est pas bien connu aujourd'hui.

Dans un script sh, vous utiliseriez généralement des expressions arithmétiques pour calculer une valeur entière, et [ … ] pour comparer les entiers.

if [ "$((x + y))" -lt "$z" ]; then …

Dans un script ksh, bash ou zsh, vous pouvez utiliser ((…)) pour les deux.

if ((x + y < z)); then …

Le [[ … ]] form est utile si vous souhaitez utiliser des conditions impliquant d'autres choses que des entiers.

Selon la page de manuel de test, = et! = Sont utilisés pour les comparaisons de chaînes tandis que les expressions -eq, -gt, -lt, -ge, -le et -ne sont des comparaisons entières. J'ai toujours suivi cette convention lors de l'écriture de scripts Shell et cela fonctionne toujours. N'oubliez pas que si vous avez des variables dans l'expression, vous devrez peut-être citer les variables d'une manière ou d'une autre pour éviter de faire une comparaison nulle.

Sur papier, nous faisons des comparaisons chaîne/nombre sans trop y penser. Un ordinateur d'autre part ne sait pas si 987 est un nombre ou une chaîne de caractères. Vous avez besoin des différents opérateurs pour dire à l'ordinateur ce qu'il faut faire pour obtenir le bon résultat. Il y a quelques informations supplémentaires ici qui expliquent une partie de l'histoire. Essentiellement, les variables ne sont pas typées et sont restées ainsi pour la compatibilité historique.

1
signal7