• Uncategorized

#### Question Detail

I’m unable to get numeric comparisons working:

``````echo "enter two numbers";

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?

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`.

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.

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`

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.

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.

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

# 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
``````

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
``````

One-line solution.

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

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+1000*parts+parts
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
exit 1
fi
``````

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!

#### You may also like...

This site uses Akismet to reduce spam. Learn how your comment data is processed.