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

前端 未结 29 1745
慢半拍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 06:59

    Wow... this is way down the list of an old question, but I think this is a pretty elegant answer. First convert each dot-separated version into its own array, using shell parameter expansion (See Shell Parameter Expansion).

    v1="05.2.3"     # some evil examples that work here
    v2="7.001.0.0"
    
    declare -a v1_array=(${v1//./ })
    declare -a v2_array=(${v2//./ })
    

    Now the two arrays have the version number as a numerical string in priority order. Lots of the above solutions take you from there, but it all derives from the observation that version string is just an integer with an arbitrary base. We can test finding the first unequal digit (like strcmp does for characters in a string).

    compare_version() {
      declare -a v1_array=(${1//./ })
      declare -a v2_array=(${2//./ })
    
      while [[ -nz $v1_array ]] || [[ -nz $v2_array ]]; do
        let v1_val=${v1_array:-0}  # this will remove any leading zeros
        let v2_val=${v2_array:-0}
        let result=$((v1_val-v2_val))
    
        if (( result != 0 )); then
          echo $result
          return
        fi
    
        v1_array=("${v1_array[@]:1}") # trim off the first "digit". it doesn't help
        v2_array=("${v2_array[@]:1}")
      done
    
      # if we get here, both the arrays are empty and neither has been numerically
      # different, which is equivalent to the two versions being equal
    
      echo 0
      return
    }
    

    This echoes a negative number if the first version is less than the second, a zero if they're equal and a positive number if the first version is greater. Some output:

    $ compare_version 1 1.2
    -2
    $ compare_version "05.1.3" "5.001.03.0.0.0.1"
    -1
    $ compare_version "05.1.3" "5.001.03.0.0.0"
    0
    $ compare_version "05.1.3" "5.001.03.0"
    0
    $ compare_version "05.1.3" "5.001.30.0"
    -27
    $ compare_version "05.2.3" "7.001.0.0"
    -2
    $ compare_version "05.1.3" "5.001.30.0"
    -27
    $ compare_version "7.001.0.0" "05.1.3"
    2
    

    Degenerate cases like, ".2" or "3.0." don't work (undefined results), and if non-numeric characters are present beside the '.' it might fail (haven't tested) but will certainly be undefined. So this should be paired with a sanitizing function or appropriate check for valid formatting. Also, I'm sure with some tweaking, this could be made more robust without too much extra baggage.

提交回复
热议问题