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
. Dans 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 faire en sorte que Bash me donne un float comme 0.5
?
Tu ne peux pas. bash seulement fait des nombres entiers; vous devez déléguer à un outil tel que bc
.
tu peux le faire:
bc <<< 'scale=2; 100/3'
33.33
UPDATE20130926
: vous pouvez utiliser:
bc -l <<< '100/3' # saves a few hits
Comme d'autres l'ont noté, bash
ne prend pas en charge l'arithmétique en virgule flottante, bien que vous puissiez la simuler avec des trucs décimaux fixes, par exemple avec deux décimales:
echo $(( 100 * 1 / 3 )) | sed 's/..$/.&/'
Sortie:
.33
Voir Nilfred's answer pour une approche similaire mais plus concise.
Outre les alternatives bc
et awk
mentionnées, il existe également les éléments suivants:
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
echo 2k 1 3 /p | dc
echo 1/3.0 | genius
echo 'pr 1/3.' | gnuplot
echo 1/3 | jq -nf /dev/stdin
Ou:
jq -n 1/3
echo 'print $(( 1/3. ))' | ksh
lua -e 'print(1/3)'
ou par stdin:
echo 'print(1/3)' | lua
echo '1/3,numer;' | maxima
avec sortie nettoyée:
echo '1/3,numer;' | maxima --quiet | sed -En '2s/[^ ]+ [^ ]+ +//p'
echo 1/3 | node -p
echo 1/3 | octave
echo print 1/3 | Perl
echo print 1/3. | python
echo 1/3 | R --no-save
avec sortie nettoyée:
echo 1/3 | R --Vanilla --quiet | sed -n '2s/.* //p'
echo print 1/3.0 | Ruby
echo 1/3 | wcalc
Avec sortie nettoyée:
echo 1/3 | wcalc | tr -d ' ' | cut -d= -f2
echo 'print $(( 1/3. ))' | zsh
Stéphane Chazelasa répondu à une question similaire over sur Unix.SX.
Améliorant 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 canaliser les variables sous forme de champs, car awk fonctionne correctement sur elles. "$ 1" et "$ 2" font référence aux premier et deuxième champs saisis dans awk.
Et vous pouvez stocker le résultat comme une autre variable en utilisant:
RESULT = `echo ...`
C'est le moment idéal pour essayer zsh, un sur-ensemble (presque) bash, avec de nombreuses autres fonctionnalités de Nice, notamment le calcul en 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 message peut vous aider: bash - La peine de passer à zsh pour un usage occasionnel?
Avant float, la logique décimale fixe é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 raison d'être du code est la suivante: multipliez par 100 avant division pour obtenir 2 décimales.
Ce n'est pas vraiment un point flottant, 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
Il existe des scénarios dans lesquels vous ne pouvez pas utiliser parce qu’il pourrait tout simplement ne pas être présent, comme dans certaines versions réduites de busybox ou de systèmes intégrés. Dans tous les cas, limiter les dépendances externes est toujours une bonne chose. Vous pouvez donc toujours ajouter 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), la division générera un nombre entier. Une fois que vous avez cet entier, traitez-le comme une chaîne et positionnez le point décimal (en le déplaçant de droite à gauche) un nombre de fois égal à la puissance de dix avec laquelle vous avez multiplié le numérateur. C'est un moyen simple d'obtenir des résultats flottants en utilisant uniquement des nombres entiers.
Si vous avez trouvé la variante de votre choix, vous pouvez également l'intégrer dans une fonction.
Ici, je suis en train d'envelopper un bashisme dans une fonction div:
Bon mot:
function div { local _d=${3:-2}; local _n=0000000000; _n=${_n:0:$_d}; local _r=$(($1$_n/$2)); _r=${_r:0:-$_d}.${_r: -$_d}; echo $_r;}
Ou multi ligne:
function div {
local _d=${3:-2}
local _n=0000000000
_n=${_n:0:$_d}
local _r=$(($1$_n/$2))
_r=${_r:0:-$_d}.${_r: -$_d}
echo $_r
}
Maintenant vous avez la fonction
div <dividend> <divisor> [<precision=2>]
et l'utiliser comme
> div 1 2
.50
> div 273 123 5
2.21951
> x=$(div 22 7)
> echo $x
3.14
je sais que c'est vieux, mais trop tentant. La réponse est donc: vous ne pouvez pas ... mais vous le pouvez en quelque sorte. Essayons ça:
$IMG_WIDTH=1024
$IMG2_WIDTH=2048
$RATIO="$(( IMG_WIDTH / $IMG2_WIDTH )).$(( (IMG_WIDTH * 100 / IMG2_WIDTH) % 100 ))
ainsi, vous obtenez 2 chiffres après le point, tronqués (appelez-le en arrondissant au chiffre inférieur, haha) en pur bash (inutile de lancer d'autres processus). Bien sûr, si vous n'avez besoin que d'un chiffre après le point, multipliez-le par 10 et modulo 10.
qu'est-ce que cela fait:
bonus track: bc version x 1000 a pris 1,8 secondes sur mon ordinateur portable, tandis que celui de pure bash a pris 0,016 secondes.
Bien que vous ne puissiez pas utiliser la division en virgule flottante dans Bash, vous pouvez utiliser la division en point fixe. Tout ce que vous devez faire est de multiplier vos entiers par une puissance de 10, puis divisez la partie entière et utilisez 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))
Utilisez calc. C'est l'exemple le plus simple que j'ai trouvé:
calc 1 + 1
2
calc 1/10
0.1
voici la commande awk: -F = champ separator == +
echo "2.1+3.1" | awk -F "+" '{print ($1+$2)}'