How to compare floating point / decimal / version numbers using bash in Linux

I could not find any single utility in bash which can do such comparison for versions or decimals or floating point numbes unless it is an integer.

Although if you want to do simple comparison i.e. if two floating/version numbers are equal or un-equal then you have plenty of tools.

For example "bc" utility but its not 100% reliable for all scenarios although it can be handly if you just want to check if the two numbers are equal or un-equal.

For example in the below script I have two values
#!/bin/bash

num1=2.21
num2=2.31

_output=`echo "$num1 != $num2" | bc`
if [ $_output == "1" ]; then
   echo "$num1 is not equal to $num2"
else
   echo "$num1 is equal to $num2"
fi

If I execute the above script
# /tmp/compare.sh
2.21 is not equal to 2.31
So that was sort of a straight forward case, lets spice it up a little bit with more complex comparison
#!/bin/bash

num1=2.21
num2=2.30

_output=`echo "$num1 >= $num2" | bc`
if [ $_output == "1" ]; then
   echo "$num1 is greater than or equal to $num2"
else
   echo "$num1 is lesser than $num2"
fi
If I execute my script, the comparison went just fine
# /tmp/compare.sh
2.21 is lesser than 2.30
But what if I made it more tricky like below
#!/bin/bash

num1=2.15
num2=2.3

_output=`echo "$num1 >= $num2" | bc`
if [ $_output == "1" ]; then
   echo "$num1 is greater than or equal to $num2"
else
   echo "$num1 is lesser than $num2"
fi
If I execute the same
# /tmp/compare.sh
2.15 is lesser than 2.3
which is wrong, 2.15 is actually greater than 2.3 so my comparison failed.

Hence with comparison checks like "less than equal to" or "greater than equal to" then we go out of luck with "bc" utility.


What if I had some version number, so will "bc" work?

Let us take the same example as above with different value
#!/bin/bash

num1=2.15.4
num2=2.3.6

_output=`echo "$num1 >= $num2" | bc`
if [ $_output == "1" ]; then
   echo "$num1 is greater than or equal to $num2"
else
   echo "$num1 is lesser than $num2"
fi
If I execute the above script I get the below error.
# /tmp/compare.sh
(standard_in) 1: syntax error
(standard_in) 1: syntax error
/tmp/compare.sh: line 7: [: ==: unary operator expected

2.15.4 is lesser than 2.3.6
The one reliable way I use is to convert the version numbers into integer values.

#!/bin/bash

function convert_to_integer {
 echo "$@" | awk -F "." '{ printf("%03d%03d%03dn", $1,$2,$3); }';
}

num1=2.15.4
num2=2.3.6

if [ "$(convert_to_integer $num1)" -gt "$(convert_to_integer $num2)" ];then
   echo "$num1 is greater than or equal to $num2"
else
   echo "$num1 is lesser than $num2"
fi
Let us try to execute the above script
# /tmp/compare.sh
2.15.4 is greater than or equal to 2.3.6
looks like this gives us the perfect comparison

Lets see what is happening in the backend
# sh -x /tmp/compare.sh
+ num1=2.15.4
+ num2=2.3.6
++ convert_to_integer 2.15.4
++ echo 2.15.4
++ awk -F . '{ printf("%03d%03d%03dn", $1,$2,$3); }'
++ convert_to_integer 2.3.6
++ echo 2.3.6
++ awk -F . '{ printf("%03d%03d%03dn", $1,$2,$3); }'
+ '[' 002015004 -gt 002003006 ']'
+ echo '2.15.4 is greater than or equal to 2.3.6'
2.15.4 is greater than or equal to 2.3.6
Here I am removing the decimal and adding "000" to make the comparison happen between integers.
You can tweak the script depending upon your requirement

Suppose you want to compare some rpm version from a Linux node

In the below script I will compare "bash" rpm version

#!/bin/bash

function convert_to_integer {
 echo "$@" | awk -F "." '{ printf("%03d%03d%03d%03d%03dn", $1,$2,$3,$4,$5); }';
}

num1=`rpm -q bash | awk -F "-" '{print $2"."$3}'`
# Make sure bash rpm version is equal to or greater than "3.2-147.30.1"
num2=3.2-147.30.1
# Split and join the version numbers with "." instead of "-"
num2=`echo $num2 | sed -e 's/-/./g'`

if [ "$(convert_to_integer $num1)" -ge "$(convert_to_integer $num2)" ];then
   echo "$num1 is greater than or equal to $num2"
else
   echo "$num1 is lesser than $num2"
fi
Lets execute our script
# sh -x /tmp/compare.sh
++ rpm -q bash
++ awk -F - '{print $2"."$3}'
+ num1=3.2.147.35.1
+ num2=3.2-147.30.1
++ echo 3.2-147.30.1
++ sed -e s/-/./g
+ num2=3.2.147.30.1
++ convert_to_integer 3.2.147.35.1
++ awk -F . '{ printf("%03d%03d%03d%03d%03dn", $1,$2,$3,$4,$5); }'
++ echo 3.2.147.35.1
++ convert_to_integer 3.2.147.30.1
++ echo 3.2.147.30.1
++ awk -F . '{ printf("%03d%03d%03d%03d%03dn", $1,$2,$3,$4,$5); }'
+ '[' 003002147035001 -ge 003002147030001 ']'
+ echo '3.2.147.35.1 is greater than or equal to 3.2.147.30.1'
3.2.147.35.1 is greater than or equal to 3.2.147.30.1

Here you have to make sure to increase the printf values to increment the variable count based on the number of values you want to compare in a version. Here for example I incremented the value to "5"

I would be happy if someone can share more tools or ways to compare such version numbers.