2013-03-05 20 views
23

Chcę porównać zmiennoprzecinkową zmienną do liczby całkowitej. Wiem, że nie jest to najlepsze z bash, ale mój cały skrypt jest już napisany w bash. Liczba $ może być dowolną liczbą całkowitą. Jeśli poniżej lub równe 50, chcę output1, dla wszystkich innych chcę mieć wyjście z inną zmienną k. To, co mam tak daleko:Porównanie zmiennoprzecinkowe ze zmienną w bash

number=43 
test=$(echo "scale=2; $number/50" | bc -l) 
echo "$test" 
for k in {1..5} 
do 
    if ["$test" -le 1] 
    then echo "output" 

    elif ["$test" -gt $k] 
    then echo "output$k" 
    fi 
done 

Gdy próbuję z testu = 0,43, pierwsza pętla w ogóle nie działa. Myślę, że ma to związek z liczbą całkowitą i zmiennoprzecinkową, ale nie może sprawić, żeby działało.

Wszystko, czego mi brakuje?

PS: ten [0.43: command not found jest tym, co wyprowadza na zacisk.

+0

można też po prostu przejść do ksh i używać przygotowana do automatycznego okrągłe. (dostępne od 1993 roku, działa dobrze) –

Odpowiedz

43

Bash nie może obsłużyć pływaków. Rura do bc zamiast:

if [ $(echo " $test > $k" | bc) -eq 1 ] 

Błąd widać jednak jest fakt, że komenda test (tj [) wymaga spacji przed i po

Jest nawet lepiej używać ((...)) skoro porównać numery tak:

if (($(bc <<< "$test > $k"))) 

część w pętli powinna wyglądać następująco:

if (($(bc <<< "$test <= 1"))) 
then 
    echo "output" 
elif (($(bc <<< "$test > $k"))) 
then 
    echo "output$k" 
fi 

Wyrażenia relacyjne oceniają na 0, jeśli relacja jest fałszywa, a 1, jeśli relacja jest prawdziwa [source]. Należy jednak zauważyć, że jest to zachowanie GNU bc i nie jest to kompilacja POSIX.

+1

Również: bash: [: 1.3: oczekiwane wyrażenie całkowite będzie błędem, jeśli poprawnie sformatowałeś wyrażenie []. + 1 @ user000001 –

+0

Wielkie dzięki! To działa. Drobne pytanie, próbuję zajrzeć. Chciałem, żeby pierwszy był mniejszy lub równy 1, a drugi ściśle wyższy niż k. Czy mogę napisać (($ (bc <<< "$ test <1" i & "$ test = 1") == 1)) lub (($ (bc <<< "$ test <= 1") == 1))? – user1983400

+0

Drugi jest poprawny. Pierwsze byłoby poprawne, cytowane w ten sposób: '(($ (bc <<<" $ test <1 && $ test == 1 ") == 1))' (nore też '=='). Aby uzyskać więcej informacji na temat wyrażeń boolowskich w 'bc' zobacz tutaj: http://www.gnu.org/software/bc/manual/html_mono/bc.html#SEC12 – user000001

9

Rodzaj starego pytania, ale wydaje mi się, że to dodatkowa odpowiedź.

Podczas pracy instalacji do kalkulatora o wyższej precyzji (bc lub dc) kosztem widelca i dodatkowym procesem, ponieważ te kalkulatory nie są wbudowane w bash. Jedną z rzeczy, które są wbudowane, jest printf. Więc jeśli można radzić sobie ze swoimi numery będąc ciągu określonej liczby miejsc po przecinku, można „fałszywe” floating point porównań z funkcji takich jak to:

#!/usr/bin/env bash 

function [[[() { 
    local LANG=C lhs rhs 
    printf -v lhs '%07.3f' "$1"; lhs=${lhs/./} 
    printf -v rhs '%07.3f' "$3"; rhs=${rhs/./} 
    case "$2" in 
    -lt) return $((! (10#$lhs < 10#$rhs))) ;; 
    -le) return $((! (10#$lhs <= 10#$rhs))) ;; 
    -eq) return $((! (10#$lhs == 10#$rhs))) ;; 
    -ge) return $((! (10#$lhs >= 10#$rhs))) ;; 
    -gt) return $((! (10#$lhs > 10#$rhs))) ;; 
    esac 
} 

number=${1:-43} 
test=$(dc -e "2k $number 50/p") 
echo "$test" 

for k in {1..5}; do 
    if [[[ "$test" -le 1 ]]]; then 
     echo "output" 
    elif [[[ "$test" -gt "$k" ]]]; then 
     echo "output $k" 
    fi 
done 

Kilka rzeczy do rozważenia tutaj.

  • Nazwałam funkcję [[[, aby była urocza. Możesz nazwać to, co chcesz. ntest lub mynumericcomparison lub nawet [[[.
  • printf to wewnętrzna funkcja w bashie, więc pomimo tego, że jest na twojej drodze, nie kosztuje rozwidlenia.
  • W obecnej wersji funkcja obsługuje numery do 999.999. Jeśli potrzebujesz większej liczby (lub większej precyzji), dostosuj formaty printf.
  • Na początku każdej zmiennej wewnątrz instrukcji case wymuszono porównanie w bazie 10, ponieważ w przeciwnym razie liczba zero-wyściełana może być interpretowana jako ósemkowa.

Zobacz także: http://mywiki.wooledge.org/BashFAQ/022

+1

To wygląda całkiem nieźle, ale ze względu na pasterzy kopiowania ustawiłem format 'printf' na coś bardziej sensownego (pływaki mogą być dość długie, a utrzymanie precyzji na niskim poziomie nie przyspieszy tej funkcji). –