Comment utiliser la division en virgule flottante dans bash?

J’essaie de diviser deux largeurs d’image dans un script Bash, mais bash me donne 0 comme résultat:

 RESULT=$(($IMG_WIDTH/$IMG2_WIDTH)) 

J’ai étudié le guide Bash et je sais que je devrais utiliser bc , dans tous les exemples sur internet, ils utilisent bc . En echo j’ai essayé de mettre la même chose dans mon SCALE mais cela n’a pas fonctionné.

Voici l’exemple que j’ai trouvé dans les tutoriels:

 echo "scale=2; ${userinput}" | bc 

Comment puis-je obtenir que Bash me donne un flottant comme 0.5 ?

Vous ne pouvez pas bash ne fait que des entiers; vous devez déléguer à un outil tel que bc .

tu peux le faire:

 bc <<< 'scale=2; 100/3' 33.33 

MISE À JOUR 20130926 : vous pouvez utiliser:

 bc -l <<< '100/3' # saves a few hits 

bash

Comme d’autres l’ont noté, bash ne prend pas en charge l’arithmétique à virgule flottante, bien que vous puissiez la simuler avec une astuce décimale fixe, par exemple avec deux décimales:

 echo $(( 100 * 1 / 3 )) | sed 's/..$/.&/' 

Sortie:

 .33 

Voir la réponse de Nilfred pour une approche similaire mais plus concise.

Des alternatives

Outre les alternatives awk et awk mentionnées, il existe également les éléments suivants:

clisp

 clisp -x '(/ 1.0 3)' 

avec sortie nettoyée:

 clisp --quiet -x '(/ 1.0 3)' 

ou par stdin:

 echo '(/ 1.0 3)' | clisp --quiet | tail -n1 

dc

 echo 2k 1 3 /p | dc 

calculasortingce de génie cli

 echo 1/3.0 | genius 

gnuplot

 echo 'pr 1/3.' | gnuplot 

jq

 echo 1/3 | jq -nf /dev/stdin 

Ou:

 jq -n 1/3 

ksh

 echo 'print $(( 1/3. ))' | ksh 

lua

 lua -e 'print(1/3)' 

ou par stdin:

 echo 'print(1/3)' | lua 

maxima

 echo '1/3,numer;' | maxima 

avec sortie nettoyée:

 echo '1/3,numer;' | maxima --quiet | sed -En '2s/[^ ]+ [^ ]+ +//p' 

nœud

 echo 1/3 | node -p 

octave

 echo 1/3 | octave 

perl

 echo print 1/3. | perl 

python

 echo print 1/3. | python 

R

 echo 1/3 | R --no-save 

avec sortie nettoyée:

 echo 1/3 | R --vanilla --quiet | sed -n '2s/.* //p' 

rbuy

 echo print 1/3.0 | ruby 

wcalc

 echo 1/3 | wcalc 

Avec sortie nettoyée:

 echo 1/3 | wcalc | tr -d ' ' | cut -d= -f2 

zsh

 echo 'print $(( 1/3. ))' | zsh 

D’autres sources

Stéphane Chazelas a répondu à une question similaire sur Unix.SX.

Améliorer un peu la réponse de marvin:

 RESULT=$(awk "BEGIN {printf \"%.2f\",${IMG_WIDTH}/${IMG2_WIDTH}}") 

bc ne vient pas toujours comme paquet installé.

Vous pouvez utiliser bc avec l’option -l (la lettre L)

 RESULT=$(echo "$IMG_WIDTH/$IMG2_WIDTH" | bc -l) 

Au lieu de bc, vous pouvez utiliser awk dans votre script.

Par exemple:

 echo "$IMG_WIDTH $IMG2_WIDTH" | awk '{printf "%.2f \n", $1/$2}' 

Dans ce qui précède, “% .2f” indique à la fonction printf de renvoyer un nombre à virgule flottante avec deux chiffres après la décimale. J’ai utilisé echo pour insérer les variables dans les champs, car awk fonctionne correctement sur ces variables. “$ 1” et “$ 2” se rapportent aux premier et deuxième champs saisis dans awk.

Et vous pouvez stocker le résultat sous une autre variable en utilisant:

 RESULT = `echo ...` 

C’est le moment idéal pour essayer zsh, un sur-ensemble (presque) bash, avec de nombreuses fonctionnalités supplémentaires, y compris des calculs à virgule flottante. Voici à quoi ressemblerait votre exemple dans zsh:

 % IMG_WIDTH=1080 % IMG2_WIDTH=640 % result=$((IMG_WIDTH*1.0/IMG2_WIDTH)) % echo $result 1.6875 

Ce post peut vous aider: bash – Vaut le passage à zsh pour un usage occasionnel?

Eh bien, avant float était une période où la logique des décimales fixes était utilisée:

 IMG_WIDTH=100 IMG2_WIDTH=3 RESULT=$((${IMG_WIDTH}00/$IMG2_WIDTH)) echo "${RESULT:0:-2}.${RESULT: -2}" 33.33 

La dernière ligne est un bashim, si vous n’utilisez pas bash, essayez plutôt ce code:

 IMG_WIDTH=100 IMG2_WIDTH=3 INTEGER=$(($IMG_WIDTH/$IMG2_WIDTH)) DECIMAL=$(tail -c 3 <<< $((${IMG_WIDTH}00/$IMG2_WIDTH))) RESULT=$INTEGER.$DECIMAL echo $RESULT 33.33 

La justification du code est la suivante: multipliez par 100 avant de diviser pour obtenir 2 décimales.

Il existe des scénarios dans lesquels vous ne pouvez pas utiliser bc car cela pourrait simplement ne pas être présent, comme dans certaines versions réduites de busybox ou des systèmes intégrés. Dans tous les cas, limiter les dépendances externes est toujours une bonne chose, vous pouvez toujours append des zéros au nombre divisé par (numérateur), ce qui revient à multiplier par une puissance de 10 (vous devez choisir une puissance de 10 selon la précision dont vous avez besoin), ce qui fera de la division un nombre entier. Une fois que cet entier le traite comme une chaîne et positionne le point décimal (en le déplaçant de droite à gauche) un nombre de fois égal à la puissance de dix, vous avez multiplié le numérateur par. C’est un moyen simple d’obtenir des résultats flottants en utilisant uniquement des nombres entiers.

Ce n’est pas vraiment un virgule flottante, mais si vous voulez quelque chose qui définit plus d’un résultat dans une invocation de bc …

 source /dev/stdin <<<$(bc <<< ' d='$1'*3.1415926535897932384626433832795*2 print "d=",d,"\n" a='$1'*'$1'*3.1415926535897932384626433832795 print "a=",a,"\n" ') echo bc radius:$1 area:$a diameter:$d 

calcule l'aire et le diamètre d'un cercle dont le rayon est donné en 1 $

Bien que vous ne puissiez pas utiliser la division en virgule flottante dans Bash, vous pouvez utiliser la division en virgule fixe. Tout ce que vous devez faire est de multiplier vos entiers par une puissance de 10, puis diviser la partie entière et utiliser une opération modulo pour obtenir la partie fractionnaire. Arrondir au besoin.

 #!/bin/bash n=$1 d=$2 # because of rounding this should be 10^{i+1} # where i is the number of decimal digits wanted i=4 P=$((10**(i+1))) Pn=$(($P / 10)) # here we 'fix' the decimal place, divide and round tward zero t=$(($n * $P / $d + ($n < 0 ? -5 : 5))) # then we print the number by dividing off the interger part and # using the modulo operator (after removing the rounding digit) to get the factional part. printf "%d.%0${i}d\n" $(($t / $P)) $(((t < 0 ? -t : t) / 10 % $Pn))