web-dev-qa-db-fra.com

Puis-je utiliser des fractions / décimales dans un script bash?

Existe-t-il une certaine syntaxe à utiliser lors de l’utilisation de fractions ou de nombres décimaux dans un script bash?

J'ai essayé d'utiliser le script suivant (sous Ubuntu 12.04):

#!/bin/bash
{
n=9
echo "$n"
for (( i=.5; $i <10; i++ ));
      do
      let "c=$i+1"
      echo $i "+" $c
done
}

Cela fonctionne avec i=1, mais il génère une erreur de syntaxe lorsque je mets dans .5.

5
user207701

Oui, bash ne gère que les entiers, mais vous pouvez contourner le problème, soit en n’utilisant pas bash (Python est très simple à prendre en charge), soit en utilisant bc pour gérer les calculs.

N'oubliez pas que votre condition dans votre boucle for ne fonctionnera pas au niveau des fractions, vous devrez donc l'étendre également.

step="0.5"

for (( i=0; $i<$(bc<<<"10/$step"); i++ )); do
      echo $(bc<<<"$step * $i")
done

Cela fera écho de 0 à 9.5 par incréments de 0.5.

6
Oli

Vous pouvez utiliser Zsh .

La plupart des shells, y compris Bash, ne supportent pas directement calculs en virgule flottante , et comme Oli l'a expliqué , la solution de contournement typique consiste à appeler bc . Cependant, vous avez plusieurs autres options .

L'un d'eux consiste à utiliser le shell Z . zsh est un shell de style Bourne ( même "famille" que Bash) qui supporte directement les calculs en virgule flottante. Donc, si vous souhaitez en faire un script Zsh au lieu d'un script Bash, vous pouvez le faire en modifiant la première ligne de votre script de _#!/bin/bash_ à _#!/bin/zsh_.

Une fois que vous faites cela, cela fonctionnera automatiquement! Dans ce cas , aucune modification supplémentaire du script n'est requise. (Parfois, une fonctionnalité Bash et la fonctionnalité Zsh correspondante fonctionnent différemment, vous devez donc apporter des modifications pour traduire un script de Bash à Zsh. Cependant, un script Bash contenant parfois des erreurs peut en fait devenir correct quand traité comme un script Zsh, en raison de différentes décisions de conception dans Zsh à propos de quoi extensions doit être effectué automatiquement.)

C'est la sortie:

_9
0.5000000000 + 1.5000000000
1.5000000000 + 2.5000000000
2.5000000000 + 3.5000000000
3.5000000000 + 4.5000000000
4.5000000000 + 5.5000000000
5.5000000000 + 6.5000000000
6.5000000000 + 7.5000000000
7.5000000000 + 8.5000000000
8.5000000000 + 9.5000000000
9.5000000000 + 10.5000000000
_

Vous aurez besoin de Zsh installé pour pouvoir fonctionner. Zsh n'est généralement pas préinstallé sur Ubuntu, mais il est fourni par le paquetage zsh. Si vous n'avez pas zsh, une erreur du type:

_-bash: ./your-script: /bin/zsh: bad interpreter: No such file or directory_

Le paquet zsh d'Ubuntu vous donne à la fois _/bin/zsh_ et _/usr/bin/zsh_. Si vous voulez que votre script soit portable sur d'autres systèmes dotés de Zsh mais à des endroits différents, vous pouvez utiliser cette ligne ligne de hachage en haut de votre script:

_#!/usr/bin/env zsh
_

Si l'utilisateur peut exécuter Zsh en tapant zsh et en appuyant sur Enter, le fait de placer cela en haut du script fonctionnera généralement (et exécutera le même exécutable zsh que celui exécuté par l'utilisateur). Cela ne fonctionnera pas sur 100% des systèmes - techniquement, env ne doit pas être fourni lui-même dans _/usr/bin_-- mais dans la pratique, il est assez rare que cela échoue, à condition que zsh est dans l'utilisateur $PATH .

Pour plus d'informations sur l'évaluation arithmétique en Zsh, y compris l'arithmétique en virgule flottante, voir 11 Évaluation arithmétique = dans le manuel en coque Z .

Précision de sortie décroissante

Une variable à virgule flottante x dans Zsh peut afficher plus de précision que vous ne le souhaitez lorsque vous la développez comme _$x_:

_% ((x=.5))
% echo "$x"
0.5000000000
_

Une façon de le développer sans ces zéros supplémentaires consiste à utiliser le développement arithmétique à la place:

_% echo "$((x))"
0.5
_

Vous pouvez également tilisez la commande printf pour afficher une valeur avec une précision limitée:

_% printf '%.2f\n' "$x"
0.50
_

Cela fonctionne également dans d'autres shells, même lorsque Shell ne prend pas en charge l'arithmétique en virgule flottante. (Certains shells n'ont pas intégré printf, mais vous pouvez toujours utiliser cette commande car ne version exécutable autonome est installé à _/usr/bin/printf_.) La commande printf n’effectuera pas d’autres opérations arithmétiques, en dehors des arrondis.

Même dans les cas où vous n'avez pas besoin de formatage numérique, printf est souvent un meilleur choix que echo dans les scripts Shell .

Si vous êtes intéressé, le manuel Zsh (voir référence ci-dessus) explique avec quelle précision les valeurs de points flottants sont réellement stockées. (Habituellement, c'est IEEE 754 binary64 .)

Vous pouvez utiliser une syntaxe plus simple (également dans Bash).

Votre script est correct tel quel, mais vous pouvez simplifier sa syntaxe pour améliorer sa lisibilité:

Laissant de côté le passage à propos de n au début (vous pouvez très bien en avoir besoin pour votre but, mais je ne peux pas dire à quoi il sert), je suggère:

_#!/bin/zsh

for ((i = .5; i < 10; i++))
do
    ((c = i + 1))
    echo "$i + $c"
done
_

Ou, si vous ne voulez pas que ces zéros supplémentaires soient imprimés:

_#!/bin/zsh

for ((i = .5; i < 10; i++))
do
    echo "$((i)) + $((i + 1))"
done
_

Cela imprime:

_0.5 + 1.5
1.5 + 2.5
2.5 + 3.5
3.5 + 4.5
4.5 + 5.5
5.5 + 6.5
6.5 + 7.5
7.5 + 8.5
8.5 + 9.5
9.5 + 10.5
_

Enfin, pour aborder un point de confusion possible:

  • Il est correct d’affecter i à _i = .5_ car elle a été évaluée en tant qu’expression arithmétique, car elle apparaît à l’intérieur de _((_ _))_ et les espaces autour de _=_ ont été autorisés. Les espaces ne sont pas requis et vous n'avez pas besoin de les utiliser, mais vous pouvez le faire. (Je l'ai fait.)
  • En dehors d'une expression arithmétique, l'affectation d'une variable Shell nécessite l'absence d'espaces de part et d'autre de _=_. C'est-à-dire que vous devez normalement écrire _var=val_ et non _var = val_. De même, à l'extérieur d'une expression arithmétique, accéder à la valeur d'une variable Shell requiert _$_ (par exemple, _$i_, _${i}_).
2
Eliah Kagan

Je ferai remarquer que, bien que bash soit limité aux nombres entiers, ksh93 ne l’est pas. Donc, ceci (exemple plus simple) fonctionne bien:

$ for ((i=0.5; i<3; i=$((i+1)))); do print $i ; done
0.5
1.5
2.5

Nous utilisons i=$((i+1)) plutôt que i++ parce que ++ ne fonctionne qu'avec des entiers.

Donc, je pense que c’est ce que veut le questionneur initial (sauf bien sûr que cela n’est pas fait avec bash):

$ for ((i=0.5; i<10; i=$((i+1)))); do c=$((i+1)) ; print $i + $c ; done
0.5 + 1.5
1.5 + 2.5
2.5 + 3.5
3.5 + 4.5
4.5 + 5.5
5.5 + 6.5
6.5 + 7.5
7.5 + 8.5
8.5 + 9.5
9.5 + 10.5

Tout cela est fait dans le shell lui-même sans avoir besoin de commandes externes. S'il est nécessaire d'utiliser bash, les suggestions qui utilisent des commandes externes sont la voie à suivre.

1
sneezy
#!/bin/bash
{ 
n=9; 
echo "$n"; 
for i in `seq .5 10`;       
do       
c=`bc <<< $i+1`;       
echo $i "+" $c; done; 
}
1
mahendra

Il existe un moyen de simuler des fractions, un peu délicat, mais il est utilisable dans certains cas:

#!/bin/bash
{
n=9
echo "$n"

for (( i=5; $i <100; i+=10 ))
do
    let "c=$i+10"
    echo "$(( $i / 10 )).$(( $i % 10 )) + $(( $c / 10 )).$(( $c % 10 ))"
done
}

Avec div/modulo, vous pouvez également effectuer quelques calculs astucieux (lire: maladroits).

1
thom

Bash lui-même ne peut pas prendre en charge les nombres à virgule flottante, mais il existe un programme appelé bc qui peut effectuer l’arithmétique décimale.

Votre script doit être réécrit pour utiliser BC (alias Best Calculator) ou un autre autre utilitaire . Alors, comment pouvez-vous faire this ? Vous ne pouvez en aucun cas utiliser la boucle for car la commande intégrée bash ne prend pas en charge les points flottants. Soit vous utilisez une autre variable pour contrôler votre boucle et utilisez 1 (ou 0) comme point de départ.

0
Braiam

La question consiste à "utiliser des fractions/nombres décimaux dans un script bash?".

Les suggestions qui "contournent le problème, ... en n'utilisant pas bash" ne répondent PAS à la question.

Ce qui suit base un échantillon décimal:

 # Un truc brutal pour les flotteurs 
 # Bash ne fait PAS les fractions/décimales. 
 # Bash effectue des entiers (nombres entiers). 
 # Une simple boucle peut résoudre ce problème, mais pas un gros problème 
 # Exemple, la division de nombres entiers ci-dessous donne une réponse flottante: 
 
 echo -n Entrez le nombre à diviser :; lire n 
 echo -n à diviser par?:; read div 
 
 mult = 1; # mettre à l'échelle le dividende, permettant ainsi un quotient de précision plus élevé 
 
 # modifier la précision dans les deux boucles 'for' ci-dessous, 
 # 2ème nombre uniquement 
 # Car ils n'acceptent pas les variables 
 
 Pour i dans {1..6}; do mult = $ mult "0"; done; # précision à 6 décimales 
 
 resultat = $ (echo -n $ ((n/div)).); # division, avec point décimal 
 temp = $ ( (n% div * mult/div)); # nombres après le signe décimal 
 
 echo $ resultat $ temp 
 
 # --------- -------- pour supprimer les zéros de fin - FACULTATIF ----------------- 
 # éventuellement, les zéros non significatifs peuvent être supprimés 
 # beaucoup de façons de le faire; J'évite de nouvelles commandes, par exemple. awk, sed, head, tail 
 # vérifier les autres sujets pour éliminer les zéros à la fin 
 
 # unset temp1 
 
 si [$ temp! = 0 ]; # zéro reste pour rester comme un flottant 
 puis 
 
 pour i dans {1..6}; # modifie la précision dans les deux boucles for. 
 
 j = $ {temp: $ ((- 0- $ i)): 1}; # recherche les zéros de fin 
 
 if [$ j! = 0]; # supprimer les zéros à la fin 
, puis 
 temp1 = $ temp1 "$ j" 
 fi 
 terminé 
 else 
 temp1 = 0 
 fi 
 
 temp1 = $ (echo $ temp1 | rev) 
 echo $ resultat $ temp1 
 
 # ----------------- CODE OPTION ----------------- 
0
Zimba