Membandingkan angka dalam Bash

546

Saya mulai belajar tentang menulis skrip untuk terminal bash, tapi saya tidak tahu cara membuat perbandingan agar berfungsi dengan benar. Script yang saya gunakan adalah:

echo "enter two numbers";
read a b;

echo "a=$a";
echo "b=$b";

if [ $a \> $b ];
then 
    echo "a is greater than b";
else
    echo "b is greater than a";
fi;

Masalahnya adalah bahwa ia membandingkan angka dari digit pertama aktif, yaitu 9 lebih besar dari 10, tetapi 1 lebih besar dari 09.

Bagaimana saya bisa mengubah angka menjadi tipe untuk melakukan perbandingan yang benar?

advert2013
sumber
1
Bacaan dasar: BashFAQ
Édouard Lopez
6
BTW, dalam bash titik koma adalah pemisah pernyataan, bukan terminator pernyataan, yang merupakan baris baru. Jadi, jika Anda hanya memiliki satu pernyataan pada satu baris, maka ;pada akhir baris berlebihan. Tidak ada salahnya, hanya buang-buang tombol (kecuali Anda menikmati mengetik semi-titik dua).
cdarke
6
Untuk memaksa angka-angka dengan angka nol di depan menjadi desimal: 10#$numberdemikian juga number=09; echo "$((10#$number))"akan menampilkan 9sementara echo $((number))akan menghasilkan kesalahan "nilai terlalu besar untuk basis".
Dijeda sampai pemberitahuan lebih lanjut.
4
Jawabannya semua memberi tahu Anda apa yang benar, tetapi tidak apa yang salah: apa yang dilakukan >operator dalam [perintah ini adalah untuk membandingkan urutan dua string yang harus diurutkan, daripada urutan mereka akan mengurutkannya sebagai angka. Anda dapat menemukan info lebih lanjut di man test.
user3035772

Jawaban:

879

Dalam bash, Anda harus melakukan pemeriksaan dalam konteks aritmatika :

if (( a > b )); then
    ...
fi

Untuk shell POSIX yang tidak mendukung (()), Anda bisa menggunakan -ltdan -gt.

if [ "$a" -gt "$b" ]; then
    ...
fi

Anda bisa mendapatkan daftar lengkap operator perbandingan dengan help testatau man test.

jordanm
sumber
7
Seperti yang dikatakan oleh @jordanm "$a" -gt "$b"adalah jawaban yang tepat. Berikut ini adalah daftar operator tes yang baik: Test Constructs .
Jeffery Thomas
Itu pasti bekerja tetapi saya masih mendapatkan "((: 09: nilai terlalu besar untuk basis (token kesalahan adalah" 09 ")" jika saya membandingkan 1 dan 09 tetapi bukan 01 dan 09 yang aneh, tetapi yang pada dasarnya telah dipecahkan masalah saya jadi terima kasih!
advert2013
8
@ advert2013 Anda tidak harus mengawali angka dengan nol. angka awalan nol adalah oktal di bash
Aleks-Daniel Jakimenko-A.
8
Waspadalah itu testadalah program apa adanya [. Jadi help testberikan informasi tentang itu. Untuk mengetahui apa yang built-in ( [[dan (() harus Anda gunakan help bashdan arahkan ke bagian itu.
RedX
1
Ekspresi aritmatika sangat bagus, tetapi operan diperlakukan sebagai ekspresi .
x-yuri
180

Polos dan sederhana

#!/bin/bash

a=2462620
b=2462620

if [ "$a" -eq "$b" ];then
  echo "They're equal";
fi

Anda dapat memeriksa lembar contekan ini jika Anda ingin lebih banyak angka perbandingan di dunia indah Bash Scripting.

Singkatnya, bilangan bulat hanya dapat dibandingkan dengan:

-eq # equal
-ne # not equal
-lt # less than
-le # less than or equal
-gt # greater than
-ge # greater than or equal
Daniel Andrei Mincă
sumber
Saya hanya membuka kancing perubahan Anda yang lain - tanda kutip ganda di sekitar "$a"dan "$b"tidak sepenuhnya diperlukan tetapi mereka adalah praktik yang baik. Kurung kurawal tidak melakukan hal yang berguna di sini.
Tom Fenech
1
cheatsheet hebat yang Anda tautkan, tidak menemukannya sebelumnya - sekarang bash sepertinya tidak begitu ajaib dan tidak dapat diprediksi lagi - terima kasih!
Ilja
apakah kutipan itu "wajib atau tidak apa- [ $a -eq $b ]apa?
derHugo
1
Kutipan @derHugo adalah opsional. Gilles memiliki penjelasan yang lebih baik tentang kapan menggunakannya unix.stackexchange.com/a/68748/50394
Daniel Andrei Mincă
1
Anda tidak perlu mengutip jika Anda menggunakan tanda kurung ganda:if [[ $a -eq $b ]];then
DrumM
38

Ada juga satu hal yang menyenangkan yang mungkin tidak diketahui oleh beberapa orang:

echo $(( a < b ? a : b ))

Kode ini akan mencetak nomor terkecil dari adanb

Aleks-Daniel Jakimenko-A.
sumber
5
Itu tidak benar. Itu juga akan mencetak bjika a == b.
konsolebox
88
@konsolebox apakah hanya saya, atau angka terkecil dari 5 dan 5 adalah 5?
Aleks-Daniel Jakimenko-A.
4
Pernyataan Anda ambigu. Bahkan menerapkan pada perintah seperti ini tidak akan dilakukan:echo "The smaller number is $(( a < b ? a : b ))."
konsolebox
4
Apa yang dia katakan adalah itu a < bmasih benar jika a == b. Saya tidak tahu semua keanehan kondisional Bash, tetapi hampir pasti ada situasi di mana ini akan membuat perbedaan.
bikemule
4
@ Bikemule Tidak, dia tidak mengatakan itu. Jika a == b, kemudian a < bdievaluasi menjadi false, itulah sebabnya ia akan mencetak b.
mapeters
21

Dalam Bash saya lebih suka melakukan ini karena alamat itu sendiri lebih sebagai operasi bersyarat tidak seperti menggunakan (( ))yang lebih dari aritmatika.

[[ N -gt M ]]

Kecuali saya melakukan hal-hal rumit seperti

(( (N + 1) > M ))

Tetapi setiap orang hanya memiliki preferensi mereka sendiri. Yang menyedihkan adalah beberapa orang memaksakan standar tidak resmi mereka.

Memperbarui:

Anda sebenarnya juga dapat melakukan ini:

[[ 'N + 1' -gt M ]]

Yang memungkinkan Anda untuk menambahkan sesuatu yang bisa Anda lakukan [[ ]]selain hal-hal aritmatika.

konsolebox
sumber
3
Ini sepertinya menyiratkan bahwa [[ ]]memaksa konteks aritmatika seperti (( )), di mana Ndiperlakukan seolah-olah itu $N, tapi saya tidak berpikir itu benar. Atau, jika itu bukan niat, penggunaan Ndan Mmembingungkan.
Benjamin W.
@ BenjaminW. Ini akan membutuhkan konfirmasi dari Chet tetapi -eq, -ne, -lt, -le, -gt, dan -ge adalah bentuk "tes aritmatika" (didokumentasikan) yang dapat menyiratkan bahwa operan tunduk pada ekspresi aritmatika sebagai baik ..
konsolebox
Terima kasih telah kembali ke ini, karena Anda sepenuhnya benar dan manual menyatakan dengan jelas: "Ketika digunakan dengan [[perintah, Arg1dan Arg2dievaluasi sebagai ekspresi aritmatika [...]".
Benjamin W.
Saya miliki NUMBER=0.0; while [[ "$NUMBER" -lt "1.0" ]]; dodan dikatakanbash: [[: 0.0: syntax error: invalid arithmetic operator (error token is ".0")
Aaron Franke
@AaronFranke Bash aritmatika tidak mendukung desimal.
konsolebox
6

Kode ini juga dapat membandingkan pelampung. Ini menggunakan awk (ini bukan bash murni), namun ini seharusnya tidak menjadi masalah, karena awk adalah perintah POSIX standar yang kemungkinan besar dikirimkan secara default dengan sistem operasi Anda.

$ awk 'BEGIN {return_code=(-1.2345 == -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 >= -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 < -1.2345) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
1
$ awk 'BEGIN {return_code=(-1.2345 < 2) ? 0 : 1; exit} END {exit return_code}'
$ echo $?
0
$ awk 'BEGIN {return_code=(-1.2345 > 2) ? 0 : 1; exit} END {exit return_code}'
$ echo $?

Untuk membuatnya lebih singkat untuk digunakan, gunakan fungsi ini:

compare_nums()
{
   # Function to compare two numbers (float or integers) by using awk.
   # The function will not print anything, but it will return 0 (if the comparison is true) or 1
   # (if the comparison is false) exit codes, so it can be used directly in shell one liners.
   #############
   ### Usage ###
   ### Note that you have to enclose the comparison operator in quotes.
   #############
   # compare_nums 1 ">" 2 # returns false
   # compare_nums 1.23 "<=" 2 # returns true
   # compare_nums -1.238 "<=" -2 # returns false
   #############################################
   num1=$1
   op=$2
   num2=$3
   E_BADARGS=65

   # Make sure that the provided numbers are actually numbers.
   if ! [[ $num1 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num1 is not a number"; return $E_BADARGS; fi
   if ! [[ $num2 =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then >&2 echo "$num2 is not a number"; return $E_BADARGS; fi

   # If you want to print the exit code as well (instead of only returning it), uncomment
   # the awk line below and comment the uncommented one which is two lines below.
   #awk 'BEGIN {print return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}'
   awk 'BEGIN {return_code=('$num1' '$op' '$num2') ? 0 : 1; exit} END {exit return_code}'
   return_code=$?
   return $return_code
}

$ compare_nums -1.2345 ">=" -1.2345 && echo true || echo false
true
$ compare_nums -1.2345 ">=" 23 && echo true || echo false
false
Vangelis Tasoulas
sumber
1
Saya bekerja dengan jumlah besar dan bashgagal membandingkannya dengan benar (coba if (( 18446744073692774399 < 8589934592 )); then echo 'integer overflow'; fi). awkbekerja seperti pesona ( if awk "BEGIN {return_code=(18446744073692774399 > 8589934592) ? 0 : 1; exit} END {exit return_code}"; then echo 'no integer overflow'; fi).
jaume
3

Jika Anda memiliki float, Anda dapat menulis fungsi dan kemudian menggunakannya misalnya

#!/bin/bash

function float_gt() {
    perl -e "{if($1>$2){print 1} else {print 0}}"
}

x=3.14
y=5.20
if [ $(float_gt $x $y) == 1 ] ; then
    echo "do stuff with x"
else
    echo "do stuff with y"
fi
Menuntut
sumber
3

Hal-hal braket (misalnya, [[ $a -gt $b ]]atau (( $a > $b ))) tidak cukup jika Anda ingin menggunakan nomor float juga; itu akan melaporkan kesalahan sintaksis. Jika Anda ingin membandingkan angka float atau angka float dengan integer, Anda dapat menggunakan (( $(bc <<< "...") )).

Sebagai contoh,

a=2.00
b=1

if (( $(bc <<<"$a > $b") )); then 
    echo "a is greater than b"
else
    echo "a is not greater than b"
fi

Anda dapat memasukkan lebih dari satu perbandingan dalam pernyataan if. Sebagai contoh,

a=2.
b=1
c=1.0000

if (( $(bc <<<"$b == $c && $b < $a") )); then 
    echo "b is equal to c but less than a"
else
    echo "b is either not equal to c and/or not less than a"
fi

Itu membantu jika Anda ingin memeriksa apakah variabel numerik (bilangan bulat atau tidak) berada dalam rentang numerik.

LC-datacientist
sumber
Ini tidak bekerja untuk saya. Sejauh yang saya tahu, perintah bc tidak mengembalikan nilai keluar tetapi mencetak "1" jika perbandingannya benar (dan "0" sebaliknya). Saya harus menulis ini sebagai gantinya:if [ "$(bc <<<"$a > $b") == "1" ]; then echo "a is greater than b; fi
Terje Mikal
@TerjeMikal Untuk perintah Anda, maksud Anda if [ $(bc <<<"$a > $b") == "1" ]; then echo "a is greater than b"; fi? (Saya pikir perintah Anda salah ditulis.) Jika demikian, itu juga berhasil. Perintah Bash Calculator (bc) adalah perintah kalkulator dasar. Beberapa contoh penggunaan lainnya ditemukan di sini dan di sini . Saya tidak tahu mengapa perintah teladan saya tidak bekerja untuk Anda.
LC-datacientist
2

Saya memecahkan ini dengan menggunakan fungsi kecil untuk mengonversi string versi ke nilai integer biasa yang dapat dibandingkan:

function versionToInt() {
  local IFS=.
  parts=($1)
  let val=1000000*parts[0]+1000*parts[1]+parts[2]
  echo $val
}

Ini membuat dua asumsi penting:

  1. Input adalah " string SemVer normal "
  2. Setiap bagian adalah antara 0-999

Sebagai contoh

versionToInt 12.34.56  # --> 12034056
versionToInt 1.2.3     # -->  1002003

Contoh pengujian apakah npmperintah memenuhi persyaratan minimum ...

NPM_ACTUAL=$(versionToInt $(npm --version))  # Capture npm version
NPM_REQUIRED=$(versionToInt 4.3.0)           # Desired version
if [ $NPM_ACTUAL \< $NPM_REQUIRED ]; then
  echo "Please update to npm@latest"
  exit 1
fi
broofa
sumber
dengan 'sort -V' Anda dapat mengurutkan nomor versi dan kemudian memutuskan apa yang harus dilakukan. Anda dapat menulis fungsi perbandingan seperti ini: function version_lt () {test "$ (printf '% s \ n'" $ @ "| sort -V | head -n 1)" == "$ 1"; } dan gunakan seperti ini: jika version_lt $ v1 $ v2; lalu ...
koem