bashversioning

How to compare two strings in dot separated version format in Bash?


Is there any way to compare such strings on bash, e.g.: 2.4.5 and 2.8 and 2.4.5.1?


Solution

  • Here is a pure Bash version that doesn't require any external utilities:

    #!/bin/bash
    vercomp () {
        if [[ $1 == $2 ]]
        then
            return 0
        fi
        local IFS=.
        local i ver1=($1) ver2=($2)
        # fill empty fields in ver1 with zeros
        for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
        do
            ver1[i]=0
        done
        for ((i=0; i<${#ver1[@]}; i++))
        do
            if ((10#${ver1[i]:=0} > 10#${ver2[i]:=0}))
            then
                return 1
            fi
            if ((10#${ver1[i]} < 10#${ver2[i]}))
            then
                return 2
            fi
        done
        return 0
    }
    
    testvercomp () {
        vercomp $1 $2
        case $? in
            0) op='=';;
            1) op='>';;
            2) op='<';;
        esac
        if [[ $op != $3 ]]
        then
            echo "FAIL: Expected '$3', Actual '$op', Arg1 '$1', Arg2 '$2'"
        else
            echo "Pass: '$1 $op $2'"
        fi
    }
    
    # Run tests
    # argument table format:
    # testarg1   testarg2     expected_relationship
    echo "The following tests should pass"
    while read -r test
    do
        testvercomp $test
    done << EOF
    1            1            =
    2.1          2.2          <
    3.0.4.10     3.0.4.2      >
    4.08         4.08.01      <
    3.2.1.9.8144 3.2          >
    3.2          3.2.1.9.8144 <
    1.2          2.1          <
    2.1          1.2          >
    5.6.7        5.6.7        =
    1.01.1       1.1.1        =
    1.1.1        1.01.1       =
    1            1.0          =
    1.0          1            =
    1.0.2.0      1.0.2        =
    1..0         1.0          =
    1.0          1..0         =
    EOF
    
    echo "The following test should fail (test the tester)"
    testvercomp 1 1 '>'
    

    Run the tests:

    $ . ./vercomp
    The following tests should pass
    Pass: '1 = 1'
    Pass: '2.1 < 2.2'
    Pass: '3.0.4.10 > 3.0.4.2'
    Pass: '4.08 < 4.08.01'
    Pass: '3.2.1.9.8144 > 3.2'
    Pass: '3.2 < 3.2.1.9.8144'
    Pass: '1.2 < 2.1'
    Pass: '2.1 > 1.2'
    Pass: '5.6.7 = 5.6.7'
    Pass: '1.01.1 = 1.1.1'
    Pass: '1.1.1 = 1.01.1'
    Pass: '1 = 1.0'
    Pass: '1.0 = 1'
    Pass: '1.0.2.0 = 1.0.2'
    Pass: '1..0 = 1.0'
    Pass: '1.0 = 1..0'
    The following test should fail (test the tester)
    FAIL: Expected '>', Actual '=', Arg1 '1', Arg2 '1'