Parenthèse en calcul arithmétique: 3 * (2 + 1)

60

expr ne semble pas aimer les parenthèses (utilisées en mathématiques selon la priorité explicite de l'opérateur):

expr 3 * (2 + 1)
bash: syntax error near unexpected token `('

Comment exprimer la priorité de l'opérateur dans bash?

Nicolas Raoul
la source

Réponses:

40

Une autre façon d'utiliser letbash builtin:

$ let a="3 * (2 + 1)"
$ printf '%s\n' "$a"
9

Remarque

Comme @ Stéphane Chazelas l'a souligné , bashvous devez utiliser ((...))pour effectuer des calculs arithmétiques exprou letpour améliorer la lisibilité.

Pour la portabilité, utilisez $((...))comme @Bernhard answer .

cuonglm
la source
1
+1 Encore plus lisible! J'ai posté ma question + réponse, pensant que cela serait utile pour mes collègues utilisateurs de Linux, mais maintenant, je profite pleinement des autres réponses :-)
Nicolas Raoul
3
Il n'y a aucune raison d'utiliser let. Ce n'est pas plus standard ou portable que (( a = 3 * (2 + 1) ))(les deux viennent de kshet ne sont disponibles qu'en ksh, bash et zsh) et il est moins lisible ou facile à citer. Utilisez a=$((3 * (2 + 1)))pour être portable.
Stéphane Chazelas
2
Je ne dis pas que c'est faux, je dis simplement que cela ne devrait pas être utilisé car il existe de meilleures alternatives (une pour la lisibilité ((a = 3 * (2 + 1) )), une pour la portabilité a=$((3 * (2 + 1)))), donc ce n'est pas une note contre vous ou votre réponse mais contre le fait qu'elle soit la réponse choisie et meilleur buteur.
Stéphane Chazelas
@ StéphaneChazelas: Mise à jour ma réponse!
jeudi
J'ai toujours utilisé a=1 $[a+2]ou a=1 b=2 $[a+b]. Est-ce leur raison d'éviter cette syntaxe?
Gordon
74

Vous pouvez utiliser plutôt l’expansion arithmétique.

echo "$(( 3 * ( 2 + 1 ) ))"
9

À mon avis, cela semble un peu plus agréable que d’utiliser expr.

De man bash

Expansion arithmétique Une expansion arithmétique permet l'évaluation d'une expression arithmétique et la substitution du résultat. Le format de développement arithmétique est le suivant:

         $((expression))

L'expression est traitée comme si elle se trouvait entre guillemets doubles, mais les guillemets doubles entre parenthèses ne font pas l'objet d'un traitement particulier. Tous les jetons de l'expression subissent une extension de paramètre, une extension de chaîne, une substitution de commande et une suppression de devis. Les développements arithmétiques peuvent être imbriqués.

L’évaluation est effectuée selon les règles énumérées ci-dessous sous ÉVALUATION ARITHMÉTIQUE. Si expression n'est pas valide, bash affiche un message indiquant un échec et aucune substitution n'est effectuée.

Bernhard
la source
1
En plus de la lisibilité, il n’est pas nécessaire de recourir à un processus supplémentaire pour effectuer le calcul; c'est géré par le shell lui-même.
Chepner
Notez que dans les shells POSIX, il est sujet au fractionnement des mots, c’est donc une bonne habitude de le citer dans des contextes de liste.
Stéphane Chazelas
Quand j'essaie cela au niveau de la coque bash, je reçois un nom de variable illégal. "
lordhog
40

Il n'y a aucune raison d'utiliser l' exprarithmétique dans les coques modernes.

POSIX définit l' $((...))opérateur d'extension. Vous pouvez donc utiliser cela dans tous les shells compatibles POSIX (les shplus modernes, comme dash, dash, bash, yash, mksh, zsh, posh, ksh ...).

a=$(( 3 * (2 + 1) ))
a=$((3*(2+1)))

ksha également introduit une commande letintégrée qui est transmise au même type d'expression arithmétique, ne se développe pas en quelque chose, mais retourne un statut de sortie basé sur le fait que l'expression soit résolue ou non, comme dans expr:

if let 'a = 3 * (2 + 1)'; then
  echo "$a is non-zero"
fi

Cependant, comme les citations le rendent maladroit et pas très lisible (pas dans la même mesure que exprbien sûr), kshintroduit également une ((...))forme alternative:

if (( a = 3 * (2 + 1) )) && (( 3 > 1 )); then
  echo "$a is non-zero and 3 > 1"
fi
((a+=2))

ce qui est beaucoup plus lisible et devrait être utilisé à la place.

letet ((...))ne sont disponibles que dans ksh, zshet bash. La $((...))syntaxe doit être privilégiée si la portabilité vers d'autres shells est nécessaire, si elle exprest utilisée uniquement pour les shells pré-POSIX de type Bourne (généralement le shell Bourne ou les premières versions du shell Almquist).

Sur le front non Bourne, il y a quelques obus avec un opérateur arithmétique intégré:

  • csh/ tcsh(réellement le premier shell Unix avec évaluation arithmétique intégrée):

    @ a = 3 * (2 + 1)
  • akanga(basé sur rc)

    a = $:'3 * (2 + 1)'
  • Comme note historique, la version originale du shell Almquist, telle que publiée sur usenet en 1989, comportait une fonction exprintégrée (fusionnée avec test), mais elle a été supprimée ultérieurement.

Stéphane Chazelas
la source
J'apprends quelque chose de nouveau chaque jour auprès de toi, Stéphane. J'apprécie beaucoup votre connaissance du shell POSIX!
MattBianco
Que diriez- : $((a = a*2))vous
Arthur2e5
Et si j'ai un point flottant? Mon expression est a = $ ((-14 + 0,2 * (1 + 2 + 3))). Le jeton d'erreur est ".2 * (1 + 2 + 3)"
Blaise
@Blaise, alors vous aurez besoin d’un shell qui supporte les points flottants $((...))comme zsh, ksh93 ou yash.
Stéphane Chazelas Le
16

exprest une commande externe, ce n’est pas une syntaxe de shell spéciale. Par conséquent, si vous souhaitez exprvoir les caractères spéciaux du shell, vous devez les protéger de l'analyse syntaxique du shell en les citant. De plus, exprchaque numéro et chaque opérateur doivent être passés en tant que paramètre séparé. Ainsi:

expr 3 \* \( 2 + 1 \)

Sauf si vous travaillez sur un système Unix antique des années 1970 ou 1980, il y a très peu de raisons de l'utiliser expr. Auparavant, les coquilles n'avaient pas de méthode intégrée pour effectuer des calculs, et vous deviez appeler l' exprutilitaire à la place. Tous les shells POSIX ont l'arithmétique intégrée via la syntaxe d' expansion arithmétique .

echo "$((3 * (2 + 1)))"

La construction se $((…))développe jusqu'au résultat de l'expression arithmétique (écrite en décimal). Bash, comme la plupart des shells, ne supporte que l'arithmétique entière modulo 2 64 (ou modulo 2 32 pour les anciennes versions de bash et d'autres shells sur les machines 32 bits).

Bash offre une syntaxe de commodité supplémentaire lorsque vous souhaitez effectuer des tâches ou pour tester si une expression est 0 mais ne vous souciez pas du résultat. Cette construction existe également dans ksh et zsh mais pas dans plain sh.

((x = 3 * (2+1)))
echo "$x"
if ((x > 3)); then 

En plus de l'arithmétique entière, exproffre quelques fonctions de manipulation de chaîne. Celles-ci aussi sont assimilées aux fonctionnalités des shells POSIX, à l'exception de l'une des suivantes: expr STRING : REGEXPteste si la chaîne correspond à l'expression rationnelle spécifiée. Un shell POSIX ne peut pas le faire sans outils externes, mais bash avec [[ STRING =~ REGEXP ]](avec une syntaxe d'expression régulière différenteexprest un outil classique et utilise BRE, bash utilise ERE).

À moins que vous n'utilisiez des scripts exécutés sur des systèmes vieux de 20 ans, vous n'avez pas besoin de savoir que cela a exprdéjà existé. Utilisez l'arithmétique des coques.

Gilles, arrête de faire le mal
la source
expr foo : '\(.\)'fait aussi l'extraction de texte. bash« s BASH_REMATCHpermette d' obtenir quelque chose de similaire. Il effectue également une comparaison des chaînes, ce que POSIX [ne fait pas (bien que l'on puisse imaginer des façons d'utiliser sortcela).
Stéphane Chazelas
souligner comme un espace réservé pour la syntaxe --- vous êtes un Schemer, @Giles? :]
RubyTuesdayDONO
1
@RubyTuesdayDONO Je n'ai pas utilisé de trait de soulignement ici. Vous avez mal interprété la question U + 2026 HORIZONTAL ELLIPSIS? Si c'est le cas, essayez d'utiliser une police plus grande.
Gilles 'SO- arrête d'être méchant'
@Giles - d'accord, oui, cela ne ressemble à un trait de soulignement qu'en raison de la taille de ma police. Pour moi, "Schemer" est un complément, et ce n'est pas comme si l'ellipse par rapport au soulignement changeait le sens de toute façon… pas besoin d'être sarcastique par-dessus: /
RubyTuesdayDONO
12

Utilisez des parenthèses avec des guillemets:

expr 3 '*' '(' 2 '+' 1 ')'
9

Les guillemets empêchent bash d’interpréter la parenthèse en tant que syntaxe bash.

Nicolas Raoul
la source
2
Ce que Nicolas illustre mais n'explique pas, c'est que les jetons sur la exprligne de commande doivent être séparés par des espaces. alors; par exemple, expr 3 "*" "(2" "+" "1)" ne fonctionnera pas . (En outre, BTW, vous n'avez probablement pas besoin de citer le +.)
G-Man dit 'Réinstallez Monica'
Les parenthèses ne sont pas des mots-clés tels que whileet [[, ils sont la syntaxe. S'ils étaient des mots-clés, ils ne seraient pas interprétés comme tels dans les arguments de commande. Vous avez besoin de guillemets pour que bash ne les analyse pas mais affiche un littéral de chaîne.
Gilles 'SO- arrête d'être méchant'
1

Si vous avez bc ..

echo '3 * (2 + 1)'|bc 
9                                                                    
Rob
la source