981

I'm unable to get numeric comparisons working:

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;

The problem is that it compares the number from the first digit on, i.e., 9 is bigger than 10, but 1 is greater than 09.

How can I convert the numbers into a type to do a true comparison?

4
  • 11
    BTW, in bash a semi-colon is a statement separator, not a statement terminator, which is a new-line. So if you only have one statement on a line then the ; at end-of-line are superfluous. Not doing any harm, just a waste of keystrokes (unless you enjoy typing semi-colons). Commented Sep 15, 2013 at 21:37
  • 8
    To force numbers with leading zeros into decimals: 10#$number so number=09; echo "$((10#$number))" will output 9 while echo $((number)) will produce a "value too great for base" error. Commented Dec 4, 2013 at 17:36
  • 7
    The answers all tell you what's right, but not what's wrong: what the > operator does in the [ command is to compare the order two strings should sort in, rather than the order they would sort in as numbers. You can find more info in man test. Commented Jan 15, 2016 at 11:57
  • See also unix.stackexchange.com/q/278707/6993 Commented Nov 21, 2022 at 13:53

10 Answers 10

1490

In Bash, you should do your check in an arithmetic context:

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

For POSIX shells that don't support (()), you can use -lt and -gt.

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

You can get a full list of comparison operators with help test or man test.

Sign up to request clarification or add additional context in comments.

23 Comments

As said by @jordanm "$a" -gt "$b" is the right answer. Here is a good list of test operator: Test Constructs.
@advert2013 you shouldn't prefix numbers with zeros. zero-prefixed numbers are octal in bash
Beware that test is a program as is [. So help test gives information about that. To find out what the built-ins ([[ and (() do you should use help bash and navigate to that part.
Arithmetic expressions are great, but operands are treated as expressions.
@AaronFranke math in bash only works with integers, it doesn't do floating point or decimals.
|
358

Like this:

#!/bin/bash

a=2462620
b=2462620

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

Integers can be compared with these operators:

-eq # Equal
-ne # Not equal
-lt # Less than
-le # Less than or equal
-gt # Greater than
-ge # Greater than or equal

See this cheatsheet.

8 Comments

I just undid your other change - the double quotes around "$a" and "$b" aren't strictly necessary but they are good practice. Curly braces don't do anything useful here.
great cheatsheet that you linked, didn't find it before - now bash doesn't seem so magic and unpredictable anymore - thank you!
are the quotes " mandatory or is just [ $a -eq $b ] also fine?
@derHugo quotes are optional. Gilles has a better explanation on when to use them unix.stackexchange.com/a/68748/50394
You don't need quotes if you use double brackets: if [[ $a -eq $b ]];then
|
64

There is also one nice thing some people might not know about:

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

This code will print the smallest number out of a and b

8 Comments

That's not true. It would also print b if a == b.
@konsolebox is it just me, or the smallest number out of 5 and 5 is 5?
Your statement is ambiguous. Even applying on a command like this won't do: echo "The smaller number is $(( a < b ? a : b ))."
What he's saying is that a < b is still true if a == b. I don't know all of the vagaries of Bash's conditionals, but there are almost certainly situations where this would make a difference.
@bikemule No, he's not saying that. If a == b, then a < b evaluates to false, which is why it would print b.
|
48

In Bash I prefer doing this as it addresses itself more as a conditional operation unlike using (( )) which is more of arithmetic.

[[ n -gt m ]]

Unless I do complex stuff like

(( (n + 1) > m ))

But everyone just has their own preferences. Sad thing is that some people impose their unofficial standards.

You can also do this:

[[ 'n + 1' -gt m ]]

Which allows you to add something else which you could do with [[ ]] besides arithmetic stuff.

5 Comments

This seems to imply that [[ ]] forces an arithmetic context like (( )), where N gets treated as if it were $N, but I don't think that's correct. Or, if that wasn't the intention, the usage of N and M is confusing.
@BenjaminW.This would require confirmation from Chet but -eq, -ne, -lt, -le, -gt, and -ge are forms of "arithmetic tests" (documented) which could imply that the operands are subject to arithmetic expressions as well..
Thanks for coming back to this, as you're completely right and the manual clearly states it: "When used with the [[ command, Arg1 and Arg2 are evaluated as arithmetic expressions [...]".
I have NUMBER=0.0; while [[ "$NUMBER" -lt "1.0" ]]; do and it says bash: [[: 0.0: syntax error: invalid arithmetic operator (error token is ".0")
@AaronFranke Bash arithmetic doesn't support decimals.
19

One-line solution.

a=2
b=1
[[ ${a} -gt ${b} ]] && echo "true" || echo "false"

gt reference: https://www.gnu.org/software/bash/manual/html_node/Bash-Conditional-Expressions.html

&& reference: https://www.gnu.org/software/bash/manual/html_node/Shell-Arithmetic.html

[[...]] construct reference: https://www.gnu.org/software/bash/manual/bash.html#index-_005b_005b

${} reference: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02 (2.6.2)

The format for parameter expansion is as follows:

${expression}

where expression consists of all characters until the matching '}'. Any '}' escaped by a or within a quoted string, and characters in embedded arithmetic expansions, command substitutions, and variable expansions, shall not be examined in determining the matching '}'.

The simplest form for parameter expansion is:

${parameter}

2 Comments

An explanation would be in order. E.g., what is the idea/gist? Is it a way to implement the ternary operator? From the Help Center: "...always explain why the solution you're presenting is appropriate and how it works". Please respond by editing (changing) your answer, not here in comments (without "Edit:", "Update:", or similar - the answer should appear as if it was written today).
Also one-line solution: a=2; b=1; if [[ ${a} -gt ${b} ]]; then echo "true"; else echo "false"; fi
13

The bracket stuff (e.g., [[ $a -gt $b ]] or (( $a > $b )) ) isn't enough if you want to use float numbers as well; it would report a syntax error. If you want to compare float numbers or float number to integer, you can use (( $(bc <<< "...") )).

For example,

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

You can include more than one comparison in the if statement. For example,

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

That's helpful if you want to check if a numeric variable (integer or not) is within a numeric range.

2 Comments

This does not work for me. As far as I can tell, the bc command does not return an exit value but instead prints "1" if the comparison is true (and "0" otherwise). I have to write this instead: if [ "$(bc <<<"$a > $b") == "1" ]; then echo "a is greater than b; fi
@TerjeMikal For your command, do you mean if [ $(bc <<<"$a > $b") == "1" ]; then echo "a is greater than b"; fi? (I think your command was mis-written.) If so, that works, too. The Bash Calculator (bc) command is a basic calculator command. Some more usage examples found here and here. I don't know why my example command didn't work for you though.
8

This code can also compare floats. It is using AWK (it is not pure Bash). However, this shouldn't be a problem, as AWK is a standard POSIX command that is most likely shipped by default with your operating system.

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

To make it shorter for use, use this function:

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 Comment

I'm working with large numbers and bash fails to compare them properly (try if (( 18446744073692774399 < 8589934592 )); then echo 'integer overflow'; fi). awk works like a charm (if awk "BEGIN {return_code=(18446744073692774399 > 8589934592) ? 0 : 1; exit} END {exit return_code}"; then echo 'no integer overflow'; fi).
8

If you have floats, you can write a function and then use that. For example,

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

Comments

5

Just adding to all the above answers:

If you have more than one expression in single if statement, you can do something like this:

if (( $a % 2 == 0 )) && (( $b % 2 != 0));
  then
  echo "What you want to do"
fi

Hope this helps!

1 Comment

This following syntax is also working: v=1; if (( $v < 0 || $v > 3 )); then echo 'inside then'; else echo 'inside else'; fi
4

I solved this by using a small function to convert version strings to plain integer values that can be compared:

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

This makes two important assumptions:

  1. The input is a "normal SemVer string"
  2. Each part is between 0-999

For example

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

Example testing whether npm command meets the minimum requirement...

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

2 Comments

with 'sort -V' you can sort version numbers and then decide what to do then. You can write a compare function like this: function version_lt() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" == "$1"; } and use it like this: if version_lt $v1 $v2; then ...
What if the version string numbers have leading zeros? Will they be interpreted as octal numbers?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.