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

前端 未结 29 1342
慢半拍i
慢半拍i 2020-11-22 06:52

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

相关标签:
29条回答
  • 2020-11-22 07:06

    There probably is no universally correct way to achieve this. If you are trying to compare versions in the Debian package system try dpkg --compare-versions <first> <relation> <second>.

    0 讨论(0)
  • 2020-11-22 07:06

    Here is another pure bash solution without any external calls:

    #!/bin/bash
    
    function version_compare {
    
    IFS='.' read -ra ver1 <<< "$1"
    IFS='.' read -ra ver2 <<< "$2"
    
    [[ ${#ver1[@]} -gt ${#ver2[@]} ]] && till=${#ver1[@]} || till=${#ver2[@]}
    
    for ((i=0; i<${till}; i++)); do
    
        local num1; local num2;
    
        [[ -z ${ver1[i]} ]] && num1=0 || num1=${ver1[i]}
        [[ -z ${ver2[i]} ]] && num2=0 || num2=${ver2[i]}
    
        if [[ $num1 -gt $num2 ]]; then
            echo ">"; return 0
        elif
           [[ $num1 -lt $num2 ]]; then
            echo "<"; return 0
        fi
    done
    
    echo "="; return 0
    }
    
    echo "${1} $(version_compare "${1}" "${2}") ${2}"
    

    And there is even more simple solution, if you are sure that the versions in question do not contain leading zeros after the first dot:

    #!/bin/bash
    
    function version_compare {
    
    local ver1=${1//.}
    local ver2=${2//.}
    
    
        if [[ $ver1 -gt $ver2 ]]; then
            echo ">"; return 0
        elif    
           [[ $ver1 -lt $ver2 ]]; then
            echo "<"; return 0
        fi 
    
    echo "="; return 0
    }
    
    echo "${1} $(version_compare "${1}" "${2}") ${2}"
    

    This will work for something like 1.2.3 vs 1.3.1 vs 0.9.7, but won't work with 1.2.3 vs 1.2.3.0 or 1.01.1 vs 1.1.1

    0 讨论(0)
  • 2020-11-22 07:07

    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 [[ -z ${ver2[i]} ]]
            then
                # fill empty fields in ver2 with zeros
                ver2[i]=0
            fi
            if ((10#${ver1[i]} > 10#${ver2[i]}))
            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'
    
    0 讨论(0)
  • 2020-11-22 07:08

    I came across and solved this problem, to add an additional (and shorter and simpler) answer...

    First note, extended shell comparison failed as you may already know...

        if [[ 1.2.0 < 1.12.12 ]]; then echo true; else echo false; fi
        false
    

    Using the sort -t'.'-g (or sort -V as mentioned by kanaka) to order versions and simple bash string comparison I found a solution. The input file contains versions in columns 3 and 4 which I want to compare. This iterates through the list identifying a match or if one is greater than the other. Hope this may still help anyone looking to do this using bash as simple as possible.

    while read l
    do
        #Field 3 contains version on left to compare (change -f3 to required column).
        kf=$(echo $l | cut -d ' ' -f3)
        #Field 4 contains version on right to compare (change -f4 to required column).
        mp=$(echo $l | cut -d ' ' -f4)
    
        echo 'kf = '$kf
        echo 'mp = '$mp
    
        #To compare versions m.m.m the two can be listed and sorted with a . separator and the greater version found.
        gv=$(echo -e $kf'\n'$mp | sort -t'.' -g | tail -n 1)
    
        if [ $kf = $mp ]; then 
            echo 'Match Found: '$l
        elif [ $kf = $gv ]; then
            echo 'Karaf feature file version is greater '$l
        elif [ $mp = $gv ]; then
            echo 'Maven pom file version is greater '$l
       else
           echo 'Comparison error '$l
       fi
    done < features_and_pom_versions.tmp.txt
    

    Thanks to Barry's blog for the sort idea... ref: http://bkhome.org/blog/?viewDetailed=02199

    0 讨论(0)
  • 2020-11-22 07:10

    This is also a pure bash solution, as printf is a bash builtin.

    function ver()
    # Description: use for comparisons of version strings.
    # $1  : a version string of form 1.2.3.4
    # use: (( $(ver 1.2.3.4) >= $(ver 1.2.3.3) )) && echo "yes" || echo "no"
    {
        printf "%02d%02d%02d%02d" ${1//./ }
    }
    
    0 讨论(0)
  • 2020-11-22 07:12

    GNU sort has an option for it:

    printf '2.4.5\n2.8\n2.4.5.1\n' | sort -V
    

    gives:

    2.4.5
    2.4.5.1
    2.8
    
    0 讨论(0)
提交回复
热议问题