¿Cómo uso la división de coma flotante en bash?

Estoy tratando de dividir dos anchos de imagen en un script Bash, pero bash me da 0 como resultado:

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

Estudié la guía Bash y sé que debería usar bc , en todos los ejemplos de internet usan bc . En echo intenté poner lo mismo en mi SCALE pero no funcionó.

Este es el ejemplo que encontré en los tutoriales:

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

¿Cómo puedo conseguir que Bash me dé una flotación como 0.5 ?

No puedes. bash solo hace números enteros; debe delegar en una herramienta como bc .

Puedes hacerlo:

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

ACTUALIZACIÓN 20130926 : puede usar:

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

bash

Como notaron otros, bash no admite la aritmética de coma flotante, aunque puedes simularlo con algunos trucos decimales fijos, por ejemplo, con dos decimales:

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

Salida:

 .33 

Vea la respuesta de Nilfred para un enfoque similar pero más conciso.

Alternativas

Además de las alternativas mencionadas de bc y awk también hay las siguientes:

Clisp

 clisp -x '(/ 1.0 3)' 

con salida limpiada:

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

o mediante stdin:

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

stream continua

 echo 2k 1 3 /p | dc 

genio cli calculadora

 echo 1/3.0 | genius 

gnuplot

 echo 'pr 1/3.' | gnuplot 

jq

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

O:

 jq -n 1/3 

ksh

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

lua

 lua -e 'print(1/3)' 

o mediante stdin:

 echo 'print(1/3)' | lua 

maxima

 echo '1/3,numer;' | maxima 

con salida limpiada:

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

nodo

 echo 1/3 | node -p 

octava

 echo 1/3 | octave 

Perl

 echo print 1/3. | perl 

python

 echo print 1/3. | python 

R

 echo 1/3 | R --no-save 

con salida limpiada:

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

Ruby

 echo print 1/3.0 | ruby 

wcalc

 echo 1/3 | wcalc 

Con salida limpiada:

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

zsh

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

Otras fonts

Stéphane Chazelas respondió una pregunta similar en Unix.SX.

Mejorando un poco la respuesta de Marvin:

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

bc no viene siempre como paquete instalado.

Puede usar bc con la opción -l (la letra L)

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

Como alternativa a bc, puedes usar awk dentro de tu script.

Por ejemplo:

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

En lo anterior, “% .2f” le dice a la función printf que devuelva un número de punto flotante con dos dígitos después del lugar decimal. Utilicé echo para canalizar las variables como campos ya que awk funciona correctamente en ellas. “$ 1” y “$ 2” se refieren a los primeros y segundos campos ingresados ​​en awk.

Y puede almacenar el resultado como alguna otra variable usando:

 RESULT = `echo ...` 

Es el momento perfecto para probar zsh, un superconjunto de bash (casi), con muchas características agradables adicionales, incluidas las matemáticas de coma flotante. Aquí está cómo sería tu ejemplo en zsh:

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

Esta publicación puede ayudarte: bash: ¿vale la pena cambiar a zsh para usar de forma casual?

Bueno, antes de flotar era un tiempo donde se usaba la lógica de decimales fijos:

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

La última línea es un bashim, si no usa bash, intente este código en su lugar:

 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 

El razonamiento detrás del código es: multiplicar por 100 antes de dividir para obtener 2 decimales.

Hay escenarios en los que no se puede usar bc porque simplemente podría no estar presente, como en algunas versiones reducidas de busybox o sistemas integrados. En cualquier caso, limitar las dependencias externas siempre es una buena cosa, así que siempre puede agregar ceros al número que se divide por (numerador), que es lo mismo que multiplicar por una potencia de 10 (debe elegir una potencia de 10 según la precisión que necesita), que hará que la división produzca un número entero. Una vez que tenga ese número entero, trátelo como una cadena y coloque el punto decimal (moviéndolo de derecha a izquierda) varias veces igual a la potencia de diez para multiplicar el numerador por. Esta es una forma simple de obtener resultados flotantes usando solo números enteros.

En realidad no es un punto flotante, pero si quieres algo que establezca más de un resultado en una invocación 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 

calcula el área y el diámetro de un círculo cuyo radio se da en $ 1

Si bien no puedes usar la división de punto flotante en Bash, puedes usar la división de punto fijo. Todo lo que necesita hacer es multiplicar sus enteros por una potencia de 10 y luego dividir la parte entera y usar una operación de módulo para obtener la parte fraccionaria. Redondeando según sea necesario.

 #!/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))