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
.
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
.
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 .
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 .)
Votre script est correct tel quel, mais vous pouvez simplifier sa syntaxe pour améliorer sa lisibilité:
{
_ et _}
_ .;
_ est redondant à la fin d'une ligne.((
_ _))
_, _$i
_ peut simplement être écrit i
.((
_ _))
_ plutôt que let
. Sans un _$
_ principal, il évalue sans développer le résultat. Tous les shells ne supportent pas cette forme non-expansible, mais Bash et Zsh (et quelques autres) le font.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:
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.)=
_. 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}
_).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.
#!/bin/bash
{
n=9;
echo "$n";
for i in `seq .5 10`;
do
c=`bc <<< $i+1`;
echo $i "+" $c; done;
}
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).
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.
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 -----------------