Reputation: 1241
Iam new to shell script. I have decimal numbers , let say [2, 1.2, 3.2, 3.2.2, 3.2.3]
Here highest number is 3.2.3, but my code is returning 3.2. How do I compare numbers upto 2 decimal points?
var=3.2.2
var2=3.2.3
is this the rightway to compare?
if (( $(echo "$var2 > $var" | bc -l) ));
Please help
Upvotes: 2
Views: 133
Reputation: 207670
I am not at a computer (just iPad) but I would split the 3 components out with IFS or parameter substitution and then form a number by multiplying the first component by 10,000, the second by 100 and adding the third, then you will have two directly comparable integers.
So, a function would look like this:
#!/bin/bash
function integerversion ()
{
IFS=. read a b c <<< "$1"
[ -z $b ] && b=0
[ -z $c ] && c=0
echo $((a*10000+b*100+c))
}
# Test it out
[ $(integerversion "3") -gt $(integerversion "2.8") ] && echo "3 > 2.8"
[ $(integerversion "2.2") -gt $(integerversion "1.9") ] && echo "2.2 > 1.9"
[ $(integerversion "0.99") -lt $(integerversion "1.0") ] && echo "0.99 < 1.0"
Output
3 > 2.8
2.2 > 1.9
0.99 < 1.0
Upvotes: 1
Reputation: 46853
0
(success) if $1<$2
, 1
otherwise, and 2
if an error occured.$1<$2
means when numbers don't have the same number of dots.Here you go:
is_lt() {
# success if $1<$2
local x y sx sy i
IFS=. read -ra x <<< "$1"
IFS=. read -ra y <<< "$2"
(((sx=${#x[@]})==0 || (sy=${#y[@]})==0)) && return 2
for((i=0;i<sx && i<sy;++i)); do
[[ ${x[i]} =~ ^[[:digit:]]+$ && ${y[i]} =~ ^[[:digit:]]+$ ]] || return 2
((10#${x[i]}<10#${y[i]})) && return 0
((10#${x[i]}>10#${y[i]})) && return 1
done
return $((sx>=sy))
}
Check it:
$ is_lt 3.2.2 3.2.3; echo $?
0
$ is_lt 3.2.2 3.2.2; echo $?
1
$ is_lt 3.2.2 3.2.2.0; echo $?
0
$ is_lt 3.2.2 3.2.2.1; echo $?
0
$ is_lt 3.1 3.x; echo $?
2
$ # Error doesn't trigger if order could be determined early:
$ is_lt 3.2.2 3.2.3.x; echo $?
0
$ is_lt 3.08.09 3.8.10; echo $?
0
In your case:
var=3.2.2
var2=3.2.3
if is_lt "$var" "$var2"; then
echo "it works"
else
echo "doesn't work"
fi
Upvotes: 2
Reputation: 84579
What you are looking for is a Version Sort, which exists in various executables. Another tool that would fit the bill is sort
. However, since your values are contained in variables and not in a list of numbers, sort -t : -k 1.1n,2.2n,3.3n filename
is of little use unless you want to write the values out to a temp file and sort
them returning the results in an array (a good idea). Be that as it may, you can implement a short sort based on your v1
and v2
that will serve the purpose:
#!/bin/bash
[ -n "$1" ] && [ -n "$2" ] || {
printf "Error: insufficient input. Usage: %s ver_num1 ver_num2\n" "${0//*\//}"
exit 1
}
v1="$1" # v1
v2="$2" # v2
v1s=$v1 # saved copies of complete string
v2s=$v2 # (same for v2)
[ "$v1" = "$v2" ] && # test inputs are equal and exit
echo "$v1 = $v2" &&
exit 0
while :; do
tv1=${v1%%.*} # tmpv1 stores the first number for v1
tr1=${v1#*.} # trv1 stores the remaining digits for v1
tv2=${v2%%.*} # (same for v2)
tr2=${v2#*.}
if [ "$tv1" = "" ] || [ "$tv2" = "" ]; then # if different length and
[ -n "$tv1" ] && echo "$v1s > $v2s" && break # equal at this point
[ -n "$tv2" ] && echo "$v1s < $v1s" && break # longer string wins
fi
if [ "$tv1" -gt "$tv2" ]; then # test 1st digit
echo "$v1s > $v2s" && break # if > or <, output results and break
elif [ "$tv1" -lt "$tv2" ]; then
echo "$v1s < $v2s" && break
else # if no determination, go to next digit
v1=$tr1 # set v1, v2 to remaining digits to loop again
v2=$tr2
fi
done
exit 0
output:
$ bash 3waySort.sh 3.2.2 3.2.3
3.2.2 < 3.2.3
$ bash 3waySort.sh 3.2.3 3.2.3
3.2.3 = 3.2.3
$ bash 3waySort.sh 3.2 3.2.3
3.2 < 3.2.3
$ bash 3waySort.sh 1.2.4.7 3.2.3
3.2.3 > 1.2.4.7
Note: I've added a few additional test and set the values to be passed as arg1
and arg2
to get you started.
Upvotes: 1
Reputation: 1326782
You need to split those strings by '.' (IFS=.
) and start comparing each segment.
You can find a complete example in this answer, which allows you to determine that:
Upvotes: 2