meallhour
meallhour

Reputation: 15581

Sorting not working correctly in bash

I have a variable VarExp with following 2 values

1.5.2
1.5.3

I have another variable VarCurr with following 1 value

1.8.1

I want to compare VarCurr with VarExp and want to echo SUCCESS only when

VarCurr >= VarExp

I have written the following code but it it always returning FAILURE

VarExp='1.5.2 1.5.3'
VarCurr='1.8.1'

printf -v versions '%s\n%s' "$VarExp" "$VarCurr"
if [[ $versions = "$(sort -V <<< "$versions")" ]]; then
    echo 'FAILURE'
else
    echo 'SUCCESS'
fi

VarCurr need to be >= the lowest value contained in VarExp

Upvotes: 2

Views: 339

Answers (4)

Ed Morton
Ed Morton

Reputation: 203635

With GNU sort for -V:

$ cat tst.sh
#!/bin/bash
varExp='1.5.2 1.5.3'
varCurr=$1

minVarExp=$(printf '%s\n' $varExp | sort -V | head -1)
maxOfVers=$(printf '%s\n' "$minVarExp" "$varCurr" | sort -V | tail -1)

if [[ $maxOfVers = $varCurr ]]; then
    echo 'SUCCESS'
else
    echo 'FAILURE'
fi

$ ./tst.sh 1.8.1
SUCCESS

$ ./tst.sh 1.5.1
FAILURE

$ ./tst.sh 1.5.2
SUCCESS

Upvotes: 3

ghoti
ghoti

Reputation: 46856

If you're using bash, you can use arrays. They make dealing with lists so much easier and safer. In addition, since you can't rely on feature availability like sort -V to be portable, one option might be to convert your numbers to something that works better with bash comparisons. The following assumes that no numbers in your strings will be greater than 3 digits. Salt to taste.

#!/bin/bash

VarExp='1.5.2 1.5.3'
VarCurr='1.8.1'

a=( $VarExp )

function padsemver {
    local IFS=.
    local -a a=()
    read -a a <<<"$1"
    printf '%03d' "${a[@]}"
}

x="$(padsemver "$VarCurr")"

for i in "${a[@]}"; do
    if [[ 10#"$x" -gt 10#"$(padsemver $i)" ]]; then
        printf '%s\n' "SUCCESS"
        exit 0
    fi
done

printf '%s\n' "FAILURE"
exit 1

This uses the padsemver() function to convert, say, 1.5.3 into 001005003, which makes it compatible with a numeric comparison in test or [[. (The 10# makes sure that a number starting with a 0 will be interpreted as decimal rather than octal.)

You could of course put the whole test into its own function rather than a stand-alone script if that was what you needed.

Upvotes: 3

rojo
rojo

Reputation: 24466

I recommend using a language that can properly objectify version objects and can understand major.minor.build.revision. Here's an example bash script which borrows from Perl for version parsing:

#!/bin/bash

VarExp='1.5.2 1.5.3'
VarCurr='1.8.1'

for i in $VarExp; do {
    perl -e 'use version;exit !(version->parse('$VarCurr') >= version->parse('$i'));' && {
        echo 'SUCCESS'
        exit
    }
}; done

echo 'FAILURE'
exit

Of course it might be more graceful just to write the whole thing in Perl.

Edit: Here's another example using Python:

#!/bin/bash

VarExp='1.5.3 1.5.6'
VarCurr='1.5.3'

for i in $VarExp; do {
    python -c 'from distutils.version import LooseVersion;\
    exit(LooseVersion("'$VarCurr'") >= LooseVersion("'$i'"))' || {
        echo 'SUCCESS'
        exit
    }
}; done

echo 'FAILURE'
exit

Upvotes: 4

RavinderSingh13
RavinderSingh13

Reputation: 133528

Could you please try following and let me know if this helps you.

varExp="1.5.2
1.5.4"
VarCurr="1.8.1"
echo "$varExp" |
awk -v current="$VarCurr" '{
  curr=current
  value=$0
  gsub(/\./,"",curr)
  gsub(/\./,"",value)
  if(curr>=value){
    print "SUCCESS, value of varExp " current " is more than variable varExp " $0"."
  }
}'

Output will be as follows.

SUCCESS, value of varExp 1.8.1 is more than variable varExp 1.5.2.
SUCCESS, value of varExp 1.8.1 is more than variable varExp 1.5.4.

Assuming that your variable named varExp has numeric values in new lines so by wrapping its value in " will keep the new lines alive in it.

Upvotes: 2

Related Questions