เปรียบเทียบตัวเลขใน Bash


546

ฉันเริ่มเรียนรู้เกี่ยวกับการเขียนสคริปต์สำหรับเทอร์มินัล bash แต่ฉันไม่สามารถหาวิธีการเปรียบเทียบเพื่อให้ทำงานได้อย่างถูกต้อง สคริปต์ที่ฉันใช้คือ:

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;

ปัญหาคือว่ามันเปรียบเทียบตัวเลขจากตัวเลขหลักแรกคือ 9 คือมากกว่า 10 แต่ 1 มากกว่า 09

ฉันจะแปลงตัวเลขเป็นประเภทเพื่อทำการเปรียบเทียบจริงได้อย่างไร


1
การอ่านขั้นพื้นฐาน: BashFAQ
Édouard Lopez

6
BTW ในทุบตีเซมิโคลอนเป็นตัวคั่นคำสั่งไม่ใช่ statement terminator ซึ่งเป็นบรรทัดใหม่ ดังนั้นหากคุณมีคำสั่งเพียงคำสั่งเดียวบนบรรทัดดังนั้น;ที่ท้ายบรรทัดจะไม่จำเป็น ไม่ทำอันตรายใด ๆ เพียงแค่กดแป้นเสีย (เว้นแต่คุณจะสนุกกับการพิมพ์เซมิโคลอน)
cdarke

6
เพื่อบังคับให้ตัวเลขที่มีเลขศูนย์นำเข้าทศนิยม: 10#$numberเพื่อnumber=09; echo "$((10#$number))"ออกจะ9ในขณะที่echo $((number))จะผลิต "ค่ามากเกินไปสำหรับฐานข้อผิดพลาด"
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

4
คำตอบทั้งหมดบอกคุณว่าอะไรถูก แต่ไม่ใช่สิ่งที่ผิด: สิ่งที่>ผู้ประกอบการทำใน[คำสั่งคือการเปรียบเทียบลำดับที่สองสตริงควรเรียงลำดับแทนที่จะเป็นลำดับที่พวกเขาจะเรียงเป็นตัวเลข คุณสามารถค้นหาข้อมูลเพิ่มเติมman testได้ที่
3035772

คำตอบ:


879

ใน bash คุณควรทำการตรวจสอบในบริบททางคณิตศาสตร์ :

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

สำหรับเปลือกหอย POSIX ที่ไม่สนับสนุน(())คุณสามารถใช้และ-lt-gt

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

คุณจะได้รับรายชื่อของผู้ประกอบการเมื่อเทียบกับหรือhelp testman test


7
ดังที่ได้กล่าวโดย @jordanm "$a" -gt "$b"คือคำตอบที่ถูกต้อง นี่คือรายการที่ดีของผู้ประกอบการทดสอบ: ทดสอบ Constructs
Jeffery Thomas

มันใช้งานได้ดี แต่ฉันยังคงได้รับ "((: 09: ค่ามากเกินไปสำหรับฐาน (โทเค็นข้อผิดพลาดคือ" 09 ")" ถ้าฉันเปรียบเทียบ 1 และ 09 แต่ไม่ใช่ 01 และ 09 ซึ่งแปลก แต่ก็แก้ไขได้โดยทั่วไป ปัญหาของฉันดังนั้นขอบคุณ!
โฆษณา 20

8
@ advert2013 คุณไม่ควรใส่ตัวเลขนำหน้าด้วยเลขศูนย์ เลขศูนย์นำหน้าเป็นฐานแปดในทุบตี
Aleks-Daniel Jakimenko-A

8
ระวังว่าเป็นโปรแกรมที่เป็นอยู่test [ดังนั้นhelp testให้ข้อมูลเกี่ยวกับที่ ในการค้นหาว่ามีบิวด์อิน ( [[และ(() อะไรบ้างคุณควรใช้help bashและนำทางไปยังส่วนนั้น
RedX

1
การแสดงออกทางคณิตศาสตร์ที่ดี แต่ตัวถูกดำเนินการจะถือว่าเป็นการแสดงออก
x-yuri

179

เรียบง่าย

#!/bin/bash

a=2462620
b=2462620

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

คุณสามารถตรวจสอบสูตรนี้หากคุณต้องการการเปรียบเทียบจำนวนมากในโลกมหัศจรรย์ของ Bash Scripting

ในไม่ช้าจำนวนเต็มสามารถเปรียบเทียบกับ:

-eq # equal
-ne # not equal
-lt # less than
-le # less than or equal
-gt # greater than
-ge # greater than or equal

ฉันเพิ่งยกเลิกการเปลี่ยนแปลงอื่น ๆ ของคุณ - การพูดซ้ำสองรอบ"$a"และ"$b"ไม่จำเป็นอย่างเคร่งครัด แต่เป็นการปฏิบัติที่ดี วงเล็บปีกกาไม่ได้ทำอะไรที่เป็นประโยชน์ที่นี่
Tom Fenech

1
cheatsheet ที่ยอดเยี่ยมที่คุณเชื่อมโยงไม่พบมาก่อนตอนนี้ทุบตีดูไม่น่าอัศจรรย์และคาดเดาไม่ได้อีกต่อไป - ขอบคุณ!
Ilja

เป็นคำพูดที่"บังคับหรือเป็นเพียงแค่[ $a -eq $b ]ปรับ
derHugo

เครื่องหมาย @derHugo เป็นตัวเลือก Gilles มีคำอธิบายที่ดีกว่าว่าเมื่อใดควรใช้พวกเขาunix.stackexchange.com/a/68748/50394
Daniel Andrei Mincă

คำตอบหลัก + คำตอบนี้ = สมบูรณ์
Gabriel Staples

38

นอกจากนี้ยังมีสิ่งหนึ่งที่ดีที่บางคนอาจไม่รู้:

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

รหัสนี้จะพิมพ์จำนวนที่น้อยที่สุดaและb


5
ที่ไม่เป็นความจริง. นอกจากนี้ยังจะพิมพ์ถ้าb a == b
konsolebox

88
@ Konsolebox มันเป็นแค่ฉันหรือเลขที่เล็กที่สุดใน 5 และ 5 คือ 5?
Aleks-Daniel Jakimenko-A

4
ข้อความของคุณไม่ชัดเจน แม้แต่การใช้คำสั่งแบบนี้จะไม่ทำ:echo "The smaller number is $(( a < b ? a : b ))."
konsolebox

4
สิ่งที่เขาพูดคือว่ายังคงเป็นจริงถ้าa < b a == bฉันไม่ทราบความแตกต่างทั้งหมดของเงื่อนไขของ Bash แต่มีสถานการณ์เกือบแน่นอนที่สิ่งนี้จะสร้างความแตกต่าง
bikemule

4
@ bikemule ไม่เขาไม่ได้พูดอย่างนั้น หากa == bแล้วประเมินเท็จซึ่งเป็นเหตุผลที่มันจะพิมพ์a < b b
mapeters

21

ใน Bash ฉันชอบทำสิ่งนี้มากกว่าเพราะมันเน้นการดำเนินการตามเงื่อนไขมากกว่าการใช้(( ))เลขคณิตมากกว่า

[[ N -gt M ]]

เว้นแต่ฉันจะทำสิ่งที่ซับซ้อนเช่น

(( (N + 1) > M ))

แต่ทุกคนมีความชอบเป็นของตัวเอง สิ่งที่น่าเศร้าก็คือบางคนกำหนดมาตรฐานที่ไม่เป็นทางการ

ปรับปรุง:

คุณสามารถทำสิ่งนี้ได้จริง:

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

ซึ่งช่วยให้คุณเพิ่มสิ่งอื่นที่คุณสามารถทำได้[[ ]]นอกเหนือจากสิ่งทางคณิตศาสตร์


3
ดูเหมือนว่านี่เป็นการบอกเป็นนัยว่า[[ ]]บังคับให้มีบริบททางคณิตศาสตร์เช่น(( ))ที่ซึ่งNได้รับการปฏิบัติราวกับว่าเป็น$Nแต่ฉันไม่คิดว่ามันถูกต้อง หรือหากนั่นไม่ใช่ความตั้งใจการใช้งานNและMความสับสน
Benjamin W.

@ BenjaminW สิ่งนี้จะต้องได้รับการยืนยันจาก Chet แต่ -eq, -ne, -lt, -le, -gt และ -ge เป็นรูปแบบของ "การทดสอบทางคณิตศาสตร์" (เอกสาร) ซึ่งอาจบ่งบอกว่าตัวถูกดำเนินการอยู่ภายใต้การแสดงออกทางคณิตศาสตร์เป็น ดี ..
konsolebox

ขอบคุณที่กลับมาที่นี่เพราะคุณพูดถูกและคู่มือก็ระบุไว้อย่างชัดเจนว่า: "เมื่อใช้กับ[[คำสั่งArg1และArg2ได้รับการประเมินเป็นนิพจน์ทางคณิตศาสตร์ [... ]"
Benjamin W.

ฉันมีNUMBER=0.0; while [[ "$NUMBER" -lt "1.0" ]]; doและมันบอกว่าbash: [[: 0.0: syntax error: invalid arithmetic operator (error token is ".0")
Aaron Franke

@AaronFranke Bash เลขคณิตไม่สนับสนุนทศนิยม
konsolebox

6

รหัสนี้ยังสามารถเปรียบเทียบลอย มันกำลังใช้ awk (ไม่ใช่ pure bash) อย่างไรก็ตามสิ่งนี้ไม่ควรมีปัญหาเนื่องจาก awk เป็นคำสั่ง POSIX มาตรฐานที่มักจะจัดส่งตามค่าเริ่มต้นพร้อมกับระบบปฏิบัติการของคุณ

$ 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 $?

เพื่อให้ใช้งานได้สั้นลงใช้ฟังก์ชันนี้:

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

1
ฉันทำงานกับคนจำนวนมากและbashไม่สามารถเปรียบเทียบได้อย่างถูกต้อง (ลองif (( 18446744073692774399 < 8589934592 )); then echo 'integer overflow'; fi) awkทำงานเหมือนจับใจ ( if awk "BEGIN {return_code=(18446744073692774399 > 8589934592) ? 0 : 1; exit} END {exit return_code}"; then echo 'no integer overflow'; fi)
Jaume

3

หากคุณมีลอยคุณสามารถเขียนฟังก์ชั่นแล้วใช้มันเช่น

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

3

เนื้อหาของวงเล็บ (เช่น[[ $a -gt $b ]]หรือ(( $a > $b ))) ไม่เพียงพอหากคุณต้องการใช้ตัวเลขลอยเช่นกัน มันจะรายงานข้อผิดพลาดทางไวยากรณ์ (( $(bc <<< "...") ))หากคุณต้องการที่จะเปรียบเทียบตัวเลขลอยหรือลอยจำนวนจำนวนเต็มคุณสามารถใช้

ตัวอย่างเช่น,

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

คุณสามารถรวมการเปรียบเทียบมากกว่าหนึ่งรายการในคำสั่ง if ตัวอย่างเช่น,

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

มีประโยชน์ถ้าคุณต้องการตรวจสอบว่าตัวแปรตัวเลข (จำนวนเต็มหรือไม่) อยู่ในช่วงตัวเลขหรือไม่


สิ่งนี้ไม่ได้ผลสำหรับฉัน เท่าที่ฉันสามารถบอกได้คำสั่ง bc จะไม่ส่งคืนค่าออก แต่จะพิมพ์ "1" แทนหากการเปรียบเทียบเป็นจริง (และ "0" เป็นอย่างอื่น) ฉันต้องเขียนสิ่งนี้แทน:if [ "$(bc <<<"$a > $b") == "1" ]; then echo "a is greater than b; fi
Terje Mikal

@TerjeMikal สำหรับคำสั่งของคุณคุณหมายถึงif [ $(bc <<<"$a > $b") == "1" ]; then echo "a is greater than b"; fiอะไร? (ฉันคิดว่าคำสั่งของคุณเขียนผิด) ถ้าเป็นเช่นนั้นก็ใช้ได้เช่นกัน คำสั่ง Bash Calculator (bc) เป็นคำสั่งเครื่องคิดเลขพื้นฐาน พบตัวอย่างการใช้งานเพิ่มเติมที่นี่และที่นี่ที่นี่ฉันไม่รู้ว่าทำไมคำสั่งตัวอย่างของฉันจึงไม่ทำงานสำหรับคุณ
LC-datascientist

2

ฉันแก้ไขสิ่งนี้โดยใช้ฟังก์ชั่นขนาดเล็กเพื่อแปลงสตริงรุ่นเป็นค่าจำนวนเต็มธรรมดาที่สามารถเปรียบเทียบได้

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

นี่ทำให้สมมติฐานที่สำคัญสองข้อ:

  1. อินพุตเป็น " สตริง SemVer ปกติ "
  2. แต่ละส่วนอยู่ระหว่าง 0-999

ตัวอย่างเช่น

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

ตัวอย่างการทดสอบว่า npmคำสั่งตรงตามข้อกำหนดขั้นต่ำหรือไม่ ...

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

ด้วย 'sort -V' คุณสามารถเรียงลำดับหมายเลขเวอร์ชันจากนั้นตัดสินใจว่าจะทำอย่างไร คุณสามารถเขียนฟังก์ชันเปรียบเทียบดังนี้: function version_lt () {test "$ (printf '% s \ n'" $ @ "| sort -V | head -n 1)" == "$ 1"; } และใช้สิ่งนี้: ถ้า version_lt $ v1 $ v2; ถ้า
งั้น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.