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

前端 未结 29 1316
慢半拍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:18

    I implemented a function that returns the same results as Dennis Williamson's but uses fewer lines. It does perform a sanity check initially which causes 1..0 to fail from his tests (which I would argue should be the case) but all of his other tests pass with this code:

    #!/bin/bash
    version_compare() {
        if [[ $1 =~ ^([0-9]+\.?)+$ && $2 =~ ^([0-9]+\.?)+$ ]]; then
            local l=(${1//./ }) r=(${2//./ }) s=${#l[@]}; [[ ${#r[@]} -gt ${#l[@]} ]] && s=${#r[@]}
    
            for i in $(seq 0 $((s - 1))); do
                [[ ${l[$i]} -gt ${r[$i]} ]] && return 1
                [[ ${l[$i]} -lt ${r[$i]} ]] && return 2
            done
    
            return 0
        else
            echo "Invalid version number given"
            exit 1
        fi
    }
    
    0 讨论(0)
  • 2020-11-22 07:19

    This is for at most 4 fields in the version.

    $ function ver { printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' ' '); }
    $ [ $(ver 10.9) -lt $(ver 10.10) ] && echo hello  
    hello
    
    0 讨论(0)
  • 2020-11-22 07:19

    if it's just about to know whether one version is lower than another I came up checking whether sort --version-sort changes the order of my version strings:

        string="$1
    $2"
        [ "$string" == "$(sort --version-sort <<< "$string")" ]
    
    0 讨论(0)
  • 2020-11-22 07:19

    Here is a simple Bash function that uses no external commands. It works for version strings that have up to three numeric parts in them - less than 3 is fine as well. It can easily be extended for more. It implements =, <, <=, >, >=, and != conditions.

    #!/bin/bash
    vercmp() {
        version1=$1 version2=$2 condition=$3
    
        IFS=. v1_array=($version1) v2_array=($version2)
        v1=$((v1_array[0] * 100 + v1_array[1] * 10 + v1_array[2]))
        v2=$((v2_array[0] * 100 + v2_array[1] * 10 + v2_array[2]))
        diff=$((v2 - v1))
        [[ $condition = '='  ]] && ((diff == 0)) && return 0
        [[ $condition = '!=' ]] && ((diff != 0)) && return 0
        [[ $condition = '<'  ]] && ((diff >  0)) && return 0
        [[ $condition = '<=' ]] && ((diff >= 0)) && return 0
        [[ $condition = '>'  ]] && ((diff <  0)) && return 0
        [[ $condition = '>=' ]] && ((diff <= 0)) && return 0
        return 1
    }
    

    Here is the test:

    for tv1 in '*' 1.1.1 2.5.3 7.3.0 0.5.7 10.3.9 8.55.32 0.0.1; do
        for tv2 in 3.1.1 1.5.3 4.3.0 0.0.7 0.3.9 11.55.32 10.0.0 '*'; do
          for c in '=' '>' '<' '>=' '<=' '!='; do
            vercmp "$tv1" "$tv2" "$c" && printf '%s\n' "$tv1 $c $tv2 is true" || printf '%s\n' "$tv1 $c $tv2 is false"
          done
        done
    done
    

    A subset of the test output:

    <snip>
    
    * >= * is true
    * <= * is true
    * != * is true
    1.1.1 = 3.1.1 is false
    1.1.1 > 3.1.1 is false
    1.1.1 < 3.1.1 is true
    1.1.1 >= 3.1.1 is false
    1.1.1 <= 3.1.1 is true
    1.1.1 != 3.1.1 is true
    1.1.1 = 1.5.3 is false
    1.1.1 > 1.5.3 is false
    1.1.1 < 1.5.3 is true
    1.1.1 >= 1.5.3 is false
    1.1.1 <= 1.5.3 is true
    1.1.1 != 1.5.3 is true
    1.1.1 = 4.3.0 is false
    1.1.1 > 4.3.0 is false
    
    <snip>
    
    0 讨论(0)
  • 2020-11-22 07:20

    You can recursively split on . and compare as shown in the following algorithm, taken from here. It returns 10 if the versions are the same, 11 if version 1 is greater than version 2 and 9 otherwise.

    #!/bin/bash
    do_version_check() {
    
       [ "$1" == "$2" ] && return 10
    
       ver1front=`echo $1 | cut -d "." -f -1`
       ver1back=`echo $1 | cut -d "." -f 2-`
    
       ver2front=`echo $2 | cut -d "." -f -1`
       ver2back=`echo $2 | cut -d "." -f 2-`
    
       if [ "$ver1front" != "$1" ] || [ "$ver2front" != "$2" ]; then
           [ "$ver1front" -gt "$ver2front" ] && return 11
           [ "$ver1front" -lt "$ver2front" ] && return 9
    
           [ "$ver1front" == "$1" ] || [ -z "$ver1back" ] && ver1back=0
           [ "$ver2front" == "$2" ] || [ -z "$ver2back" ] && ver2back=0
           do_version_check "$ver1back" "$ver2back"
           return $?
       else
               [ "$1" -gt "$2" ] && return 11 || return 9
       fi
    }    
    
    do_version_check "$1" "$2"
    

    Source

    0 讨论(0)
  • For old version/busybox sort. Simple form provide roughly result and often works.

    sort -n
    

    This is escpecial useful on version which contains alpha symbols like

    10.c.3
    10.a.4
    2.b.5
    
    0 讨论(0)
提交回复
热议问题