Как сравнить две строки в формате dot separated version в Bash?


есть ли способ сравнить такие строки на bash, например:2.4.5 и 2.8 и 2.4.5.1?

26 137

26 ответов:

вот чистая версия Bash, которая не требует никаких внешних утилит:

#!/bin/bash
vercomp () {
    if [[  ==  ]]
    then
        return 0
    fi
    local IFS=.
    local i ver1=() ver2=()
    # 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  
    case $? in
        0) op='=';;
        1) op='>';;
        2) op='<';;
    esac
    if [[ $op !=  ]]
    then
        echo "FAIL: Expected '', Actual '$op', Arg1 '', Arg2 ''"
    else
        echo "Pass: ' $op '"
    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 '>'

выполнить тесты:

$ . ./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'

Если у вас есть coreutils-7 (в Ubuntu кармический, но не бойкий) , то ваш

вероятно, нет универсального правильного способа достичь этого. Если вы пытаетесь сравнить версии в системе пакетов Debian, попробуйте dpkg --compare-versions <first> <relation> <second>.

GNU sort имеет возможность для этого:

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

выдает:

2.4.5
2.4.5.1
2.8

Ну если вы знаете количество полей, которые вы можете использовать -k n, n и получить супер-простое решение

echo '2.4.5
2.8
2.4.5.1
2.10.2' | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -g

2.4.5
2.4.5.1
2.8
2.10.2

Это не более 4 полей в версии.

$ function ver { printf "%03d%03d%03d%03d" $(echo "" | tr '.' ' '); }
$ [ $(ver 10.9) -lt $(ver 10.10) ] && echo hello  
hello
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", ,,,); }'; }

используется так:

if [ $(version $VAR) -ge $(version "6.2.0") ]; then
    echo "Version is up to date"
fi

(от https://apple.stackexchange.com/a/123408/11374)

вы можете рекурсивно разделить на . и сравнить, как показано в следующем алгоритме, взяты из здесь. Он возвращает 10, Если версии одинаковы, 11, если версия 1 больше версии 2 и 9 в противном случае.

#!/bin/bash
do_version_check() {

   [ "" == "" ] && return 10

   ver1front=`echo  | cut -d "." -f -1`
   ver1back=`echo  | cut -d "." -f 2-`

   ver2front=`echo  | cut -d "." -f -1`
   ver2back=`echo  | cut -d "." -f 2-`

   if [ "$ver1front" != "" ] || [ "$ver2front" != "" ]; then
       [ "$ver1front" -gt "$ver2front" ] && return 11
       [ "$ver1front" -lt "$ver2front" ] && return 9

       [ "$ver1front" == "" ] || [ -z "$ver1back" ] && ver1back=0
       [ "$ver2front" == "" ] || [ -z "$ver2back" ] && ver2back=0
       do_version_check "$ver1back" "$ver2back"
       return $?
   else
           [ "" -gt "" ] && return 11 || return 9
   fi
}    

do_version_check "" ""

источник

Я использую встроенный Linux (Yocto) с BusyBox. BusyBox sort нет (а BusyBox expr match может делать регулярные выражения). Поэтому мне нужно было сравнить версию Bash, которая работала с этим ограничением.

Я сделал следующее (Похоже на ответ Денниса Уильямсона) для сравнения используется алгоритм типа "естественная сортировка". Он разбивает строку на числовые части и нечисловые части; он сравнивает числовые части численно (так 10 больше 9), и сравнивает нечисловые части как простое сравнение ASCII.

ascii_frag() {
    expr match "" "\([^[:digit:]]*\)"
}

ascii_remainder() {
    expr match "" "[^[:digit:]]*\(.*\)"
}

numeric_frag() {
    expr match "" "\([[:digit:]]*\)"
}

numeric_remainder() {
    expr match "" "[[:digit:]]*\(.*\)"
}

vercomp_debug() {
    OUT=""
    #echo "${OUT}"
}

# return 1 for  > 
# return 2 for  < 
# return 0 for equal
vercomp() {
    local WORK1=""
    local WORK2=""
    local NUM1="", NUM2="", ASCII1="", ASCII2=""
    while true; do
        vercomp_debug "ASCII compare"
        ASCII1=`ascii_frag "${WORK1}"`
        ASCII2=`ascii_frag "${WORK2}"`
        WORK1=`ascii_remainder "${WORK1}"`
        WORK2=`ascii_remainder "${WORK2}"`
        vercomp_debug "\"${ASCII1}\" remainder \"${WORK1}\""
        vercomp_debug "\"${ASCII2}\" remainder \"${WORK2}\""

        if [ "${ASCII1}" \> "${ASCII2}" ]; then
            vercomp_debug "ascii ${ASCII1} > ${ASCII2}"
            return 1
        elif [ "${ASCII1}" \< "${ASCII2}" ]; then
            vercomp_debug "ascii ${ASCII1} < ${ASCII2}"
            return 2
        fi
        vercomp_debug "--------"

        vercomp_debug "Numeric compare"
        NUM1=`numeric_frag "${WORK1}"`
        NUM2=`numeric_frag "${WORK2}"`
        WORK1=`numeric_remainder "${WORK1}"`
        WORK2=`numeric_remainder "${WORK2}"`
        vercomp_debug "\"${NUM1}\" remainder \"${WORK1}\""
        vercomp_debug "\"${NUM2}\" remainder \"${WORK2}\""

        if [ -z "${NUM1}" -a -z "${NUM2}" ]; then
            vercomp_debug "blank 1 and blank 2 equal"
            return 0
        elif [ -z "${NUM1}" -a -n "${NUM2}" ]; then
            vercomp_debug "blank 1 less than non-blank 2"
            return 2
        elif [ -n "${NUM1}" -a -z "${NUM2}" ]; then
            vercomp_debug "non-blank 1 greater than blank 2"
            return 1
        fi

        if [ "${NUM1}" -gt "${NUM2}" ]; then
            vercomp_debug "num ${NUM1} > ${NUM2}"
            return 1
        elif [ "${NUM1}" -lt "${NUM2}" ]; then
            vercomp_debug "num ${NUM1} < ${NUM2}"
            return 2
        fi
        vercomp_debug "--------"
    done
}

он может сравнивать более сложные номера версии, такие как

  • 1.2-r3 и 1.2-r4
  • 1.2rc3 и 1.2r4

обратите внимание, что он не возвращает тот же результат для некоторых тупиковых ситуаций в ответ Денниса Уильямсона. В в частности:

1            1.0          <
1.0          1            >
1.0.2.0      1.0.2        >
1..0         1.0          >
1.0          1..0         <

но это угловые случаи, и я думаю, что результаты все еще разумны.

если это просто о том, чтобы знать, является ли одна версия ниже, чем другая, я пришел проверить, является ли sort --version-sort изменяет порядок строк моей версии:

    string="
"
    [ "$string" == "$(sort --version-sort <<< "$string")" ]

я реализовал функцию, которая возвращает те же результаты, что и Деннис Уильямсон, но использует меньше строк. Он выполняет проверку вменяемости изначально, которая вызывает 1..0 провалить его тесты (что я бы сказал должны быть так) но все его другие тесты проходят с этим кодом:

#!/bin/bash
version_compare() {
    if [[  =~ ^([0-9]+\.?)+$ &&  =~ ^([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
}

для старой версии / busybox sort. Простая форма обеспечивает примерно результат и часто работает.

sort -n

Это escpecial полезно на версии, которая содержит альфа-символы, такие как

10.c.3
10.a.4
2.b.5

вот простая функция Bash, которая не использует никаких внешних команд. Он работает для строк версии, которые имеют до трех числовых частей в них - менее 3 отлично, а также. Его можно легко расширить для больше. Он реализует =,<,<=,>,>= и != условиях.

#!/bin/bash
vercmp() {
    version1= version2= condition=

    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
}

вот тест:

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

подмножество выходных данных теста:

<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>
$ for OVFTOOL_VERSION in "4.2.0" "4.2.1" "5.2.0" "3.2.0" "4.1.9" "4.0.1" "4.3.0" "4.5.0" "4.2.1" "30.1.0" "4" "5" "4.1" "4.3"
> do
>   if [ $(echo "$OVFTOOL_VERSION 4.2.0" | tr " " "\n" | sort --version-sort | head -n 1) = 4.2.0 ]; then 
>     echo "$OVFTOOL_VERSION is >= 4.2.0"; 
>   else 
>     echo "$OVFTOOL_VERSION is < 4.2.0"; 
>   fi
> done
4.2.0 is >= 4.2.0
4.2.1 is >= 4.2.0
5.2.0 is >= 4.2.0
3.2.0 is < 4.2.0
4.1.9 is < 4.2.0
4.0.1 is < 4.2.0
4.3.0 is >= 4.2.0
4.5.0 is >= 4.2.0
4.2.1 is >= 4.2.0
30.1.0 is >= 4.2.0
4 is < 4.2.0
5 is >= 4.2.0
4.1 is < 4.2.0
4.3 is >= 4.2.0
  • функции V - чистое решение bash, никаких внешних утилит не требуется.
  • поддерживает ===!=<<=> и >= (лексикографический).
  • опционное сравнение письма кабеля:1.5a < 1.5b
  • неравное сравнение длины:1.6 > 1.5b
  • читает слева-направо: if V 1.5 '<' 1.6; then ....

# Sample output
# Note: ++ (true) and __ (false) mean that V works correctly.

++ 3.6 '>' 3.5b
__ 2.5.7 '<=' 2.5.6
++ 2.4.10 '<' 2.5.9
__ 3.0002 '>' 3.0003.3
++ 4.0-RC2 '>' 4.0-RC1

function V() # -a -op -$b
# Compare a and b as version strings. Rules:
# R1: a and b : dot-separated sequence of items. Items are numeric. The last item can optionally end with letters, i.e., 2.5 or 2.5a.
# R2: Zeros are automatically inserted to compare the same number of items, i.e., 1.0 < 1.0.1 means 1.0.0 < 1.0.1 => yes.
# R3: op can be '=' '==' '!=' '<' '<=' '>' '>=' (lexicographic).
# R4: Unrestricted number of digits of any item, i.e., 3.0003 > 3.0000004.
# R5: Unrestricted number of items.
{
  local a= op= b= al=${1##*.} bl=${3##*.}
  while [[ $al =~ ^[[:digit:]] ]]; do al=${al:1}; done
  while [[ $bl =~ ^[[:digit:]] ]]; do bl=${bl:1}; done
  local ai=${a%$al} bi=${b%$bl}

  local ap=${ai//[[:digit:]]} bp=${bi//[[:digit:]]}
  ap=${ap//./.0} bp=${bp//./.0}

  local w=1 fmt=$a.$b x IFS=.
  for x in $fmt; do [ ${#x} -gt $w ] && w=${#x}; done
  fmt=${*//[^.]}; fmt=${fmt//./%${w}s}
  printf -v a $fmt $ai$bp; printf -v a "%s-%${w}s" $a $al
  printf -v b $fmt $bi$ap; printf -v b "%s-%${w}s" $b $bl

  case $op in
    '<='|'>=' ) [ "$a" ${op:0:1} "$b" ] || [ "$a" = "$b" ] ;;
    * )         [ "$a" $op "$b" ] ;;
  esac
}

код Объяснил

строка 1: определение локальных переменных:

  • a,op,b - сравнение операндов и оператора, т. е., "3.6" > "3,5 а".
  • al,bl - письмо хвосты a и b, инициализируется в хвостовой элемент, т. е. "6" и "5a".

строки 2, 3: левая обрезка цифр из хвостовых элементов, поэтому остаются только буквы, если таковые имеются, т. е. "" И "ля."

строка 4: правая обрезка букв от a и b оставить только последовательность числовых элементов в качестве локальных переменных ai и bi, т. е. "3.6" и "3.5". Известный пример: "4.01-RC2 в" > "4.01-НИК1" дает ИИ="4.01" Аль - ="-RC2 В" и " Би " ="4.01" бл="-НИК1".

строка 6: определение локальных переменных:

  • ap,bp - нулевые правые отступы для ai и bi. Начните с сохранения только межэлементные точки, число которых равно числу элементов a и b соответственно.

строка 7: затем добавьте "0" после каждой точки, чтобы сделать отступ маски.

строка 9: локальные переменные:

  • w - ширина элемента
  • fmt - строка формата printf, подлежащая вычислению
  • x временное
  • с IFS=. bash разбивает переменную значения в '.'.

строка 10: расчета w, максимальная ширина элемента, которая будет использоваться для выравнивания элементов для лексикографического сравнения. В нашем примере w=2.

строка 11: создайте формат выравнивания printf, заменив каждый символ $a.$b С %${w}s, т. е. "3.6" > "3.5 a" дает "%2s%2s%2s%2s".

строка 12: "printf-v a" устанавливает значение переменной a. Это эквивалентно к a=sprintf(...) во многих языках программирования. Обратите внимание, что здесь, по эффекту IFS=. аргументы к printf разделяется на отдельные элементы.

первый printf статьи a слева дополняется пробелами в то время как достаточно "0" элементы добавляются с bp чтобы убедиться, что результирующая строка a можно осмысленно сравнить с аналогично отформатированным b.

обратите внимание, что мы добавляем bp - не ap до ai, потому что ap и bp может иметь разную длину, так что это приводит к a и b имеющие равные длины.

второй printf мы добавляем часть письма al до a с достаточным заполнением для обеспечения значимого сравнения. Сейчас a готов для сравнения с b.

строка 13: то же самое, что строка 12, но для b.

строка 15: разделить случаи сравнения между не-встроенный (<= и >=) и встроенные операторы.

строка 16: если оператор сравнения проверить a<b or a=b - соответственно >=a<b or a=b

строка 17: тест для встроенных операторов сравнения.

# All tests

function P { printf "$@"; }
function EXPECT { printf "$@"; }
function CODE { awk $BASH_LINENO'==NR{print " ",,}' ""; }
P 'Note: ++ (true) and __ (false) mean that V works correctly.\n'

V 2.5    '!='  2.5      && P + || P _; EXPECT _; CODE
V 2.5    '='   2.5      && P + || P _; EXPECT +; CODE
V 2.5    '=='  2.5      && P + || P _; EXPECT +; CODE

V 2.5a   '=='  2.5b     && P + || P _; EXPECT _; CODE
V 2.5a   '<'   2.5b     && P + || P _; EXPECT +; CODE
V 2.5a   '>'   2.5b     && P + || P _; EXPECT _; CODE
V 2.5b   '>'   2.5a     && P + || P _; EXPECT +; CODE
V 2.5b   '<'   2.5a     && P + || P _; EXPECT _; CODE
V 3.5    '<'   3.5b     && P + || P _; EXPECT +; CODE
V 3.5    '>'   3.5b     && P + || P _; EXPECT _; CODE
V 3.5b   '>'   3.5      && P + || P _; EXPECT +; CODE
V 3.5b   '<'   3.5      && P + || P _; EXPECT _; CODE
V 3.6    '<'   3.5b     && P + || P _; EXPECT _; CODE
V 3.6    '>'   3.5b     && P + || P _; EXPECT +; CODE
V 3.5b   '<'   3.6      && P + || P _; EXPECT +; CODE
V 3.5b   '>'   3.6      && P + || P _; EXPECT _; CODE

V 2.5.7  '<='  2.5.6    && P + || P _; EXPECT _; CODE
V 2.4.10 '<'   2.4.9    && P + || P _; EXPECT _; CODE
V 2.4.10 '<'   2.5.9    && P + || P _; EXPECT +; CODE
V 3.4.10 '<'   2.5.9    && P + || P _; EXPECT _; CODE
V 2.4.8  '>'   2.4.10   && P + || P _; EXPECT _; CODE
V 2.5.6  '<='  2.5.6    && P + || P _; EXPECT +; CODE
V 2.5.6  '>='  2.5.6    && P + || P _; EXPECT +; CODE
V 3.0    '<'   3.0.3    && P + || P _; EXPECT +; CODE
V 3.0002 '<'   3.0003.3 && P + || P _; EXPECT +; CODE
V 3.0002 '>'   3.0003.3 && P + || P _; EXPECT _; CODE
V 3.0003.3 '<' 3.0002   && P + || P _; EXPECT _; CODE
V 3.0003.3 '>' 3.0002   && P + || P _; EXPECT +; CODE

V 4.0-RC2 '>' 4.0-RC1   && P + || P _; EXPECT +; CODE
V 4.0-RC2 '<' 4.0-RC1   && P + || P _; EXPECT _; CODE

вот еще одно чистое решение bash без каких-либо внешних вызовов:

#!/bin/bash

function version_compare {

IFS='.' read -ra ver1 <<< ""
IFS='.' read -ra ver2 <<< ""

[[ ${#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 " $(version_compare "" "") "

и есть еще более простое решение, если вы уверены, что версий не содержат ведущих нулей после первого многоточия:

#!/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 " $(version_compare "" "") "

это будет работать для чего-то вроде 1.2.3 против 1.3.1 против 0.9.7, но не будет работать с 1.2.3 против 1.2.3.0 или 1.01.1 против 1.1.1

вот уточнение верхнего ответа (Денниса), который является более кратким и использует другую схему возвращаемых значений, чтобы упростить реализацию = с помощью одного сравнения. Он также сравнивает все после первого символа не в [0-9.] лексикографически, т. 1.0rc1 проверка

# Compares two tuple-based, dot-delimited version numbers a and b (possibly
# with arbitrary string suffixes). Returns:
# 1 if a<b
# 2 if equal
# 3 if a>b
# Everything after the first character not in [0-9.] is compared
# lexicographically using ASCII ordering if the tuple-based versions are equal.
compare-versions() {
    if [[  ==  ]]; then
        return 2
    fi
    local IFS=.
    local i a=(${1%%[^0-9.]*}) b=(${2%%[^0-9.]*})
    local arem=${1#${1%%[^0-9.]*}} brem=${2#${2%%[^0-9.]*}}
    for ((i=0; i<${#a[@]} || i<${#b[@]}; i++)); do
        if ((10#${a[i]:-0} < 10#${b[i]:-0})); then
            return 1
        elif ((10#${a[i]:-0} > 10#${b[i]:-0})); then
            return 3
        fi
    done
    if [ "$arem" '<' "$brem" ]; then
        return 1
    elif [ "$arem" '>' "$brem" ]; then
        return 3
    fi
    return 2
}

я наткнулся и решил эту проблему, чтобы добавить дополнительный (и более короткий и простой) ответ...

первое Примечание, расширенное сравнение оболочки не удалось, как вы уже знаете...

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

используя сортировку-t'.'- g (или sort-V, Как упоминалось Канакой) для заказа версий и простого сравнения строк bash я нашел решение. Входной файл содержит версии в Столбцах 3 и 4, которые я хочу сравнить. Это повторяется по списку, определяющему совпадение или если оно есть больше, чем другие. Надеюсь, что это все еще может помочь всем, кто хочет сделать это с помощью bash как можно проще.

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

спасибо блогу Барри за идею сортировки... ref:http://bkhome.org/blog/?viewDetailed=02199

### the answer is does we second argument is higher
function _ver_higher {
        ver=`echo -ne "\n" |sort -Vr |head -n1`
        if [ "" == "" ]; then
                return 1
        elif [ "" == "$ver" ]; then
                return 0
        else
                return 1
        fi
}

if _ver_higher  ; then
        echo higher
else
        echo same or less
fi

Это довольно просто и мало.

Как насчет этого? Кажется, работает?

checkVersion() {
subVer1=
subVer2=

[ "$subVer1" == "$subVer2" ] && echo "Version is same"
echo "Version 1 is $subVer1"
testVer1=$subVer1
echo "Test version 1 is $testVer1"
x=0
while [[ $testVer1 != "" ]]
do
  ((x++))
  testVer1=`echo $subVer1|cut -d "." -f $x`
  echo "testVer1 now is $testVer1"
  testVer2=`echo $subVer2|cut -d "." -f $x`
  echo "testVer2 now is $testVer2"
  if [[ $testVer1 -gt $testVer2 ]]
  then
    echo "$ver1 is greater than $ver2"
    break
  elif [[ "$testVer2" -gt "$testVer1" ]]
  then
    echo "$ver2 is greater than $ver1"
    break
  fi
  echo "This is the sub verion for first value $testVer1"
  echo "This is the sub verion for second value $testVer2"
done
}

ver1=
ver2=
checkVersion "$ver1" "$ver2"

спасибо решение Денниса, мы можем расширить его для того чтобы позволить операторам сравнения'>', '='.

# compver ver1 '=|==|>|<|>=|<=' ver2
compver() { 
    local op
    vercomp  
    case $? in
        0) op='=';;
        1) op='>';;
        2) op='<';;
    esac
    [[  == *$op* ]] && return 0 || return 1
}

затем мы можем использовать операторы сравнения в выражениях типа:

compver 1.7 '<=' 1.8
compver 1.7 '==' 1.7
compver 1.7 '=' 1.7

и проверить только true / false результата, как:

if compver $ver1 '>' $ver2; then
    echo "Newer"
fi

вот еще одна чистая версия bash, а не принятый ответ. Он только проверяет, является ли версия меньше или равна "минимальной версии", и он будет проверять буквенно-цифровые последовательности лексикографически, что часто дает неправильный результат ("снимок "не позднее" выпуска", чтобы дать общий пример). Он будет отлично работать для мажора / минора.

is_number() {
    case "$BASH_VERSION" in
        3.1.*)
            PATTERN='\^\[0-9\]+$'
            ;;
        *)
            PATTERN='^[0-9]+$'
            ;;
    esac

    [[ "" =~ $PATTERN ]]
}

min_version() {
    if [[ $# != 2 ]]
    then
        echo "Usage: min_version current minimum"
        return
    fi

    A="${1%%.*}"
    B="${2%%.*}"

    if [[ "$A" != "" && "$B" != "" && "$A" == "$B" ]]
    then
        min_version "${1#*.}" "${2#*.}"
    else
        if is_number "$A" && is_number "$B"
        then
            [[ "$A" -ge "$B" ]]
        else
            [[ ! "$A" < "$B" ]]
        fi
    fi
}

я реализовал еще одну функцию компаратора. У этого было два конкретных требования: (i) я не хотел, чтобы функция отказала с помощью return 1 но echo вместо этого; (ii) поскольку мы получаем версии из репозитория git, версия "1.0" должна быть больше, чем "1.0.2", что означает, что "1.0" происходит из транка.

function version_compare {
  IFS="." read -a v_a <<< ""
  IFS="." read -a v_b <<< ""

  while [[ -n "$v_a" || -n "$v_b" ]]; do
    [[ -z "$v_a" || "$v_a" -gt "$v_b" ]] && echo 1 && return
    [[ -z "$v_b" || "$v_b" -gt "$v_a" ]] && echo -1 && return

    v_a=("${v_a[@]:1}")
    v_b=("${v_b[@]:1}")
  done

  echo 0
}

не стесняйтесь комментировать и предлагать усовершенствования.

другой подход (модифицированная версия @joynes), который сравнивает пунктирные версии, как задано в вопросе
(т. е. "1.2", "2.3.4", "1.0", "1.10.1", и т.д.).
Максимальное количество позиций должно быть известно заранее. Подход ожидает максимум 3 позиции версии.

expr $(printf "\n" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != 

пример использования:

expr $(printf "1.10.1\n1.7" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.7"

возвращает: 1, так как 1.10.1 больше, чем 1,7

expr $(printf "1.10.1\n1.11" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -g | sed -n 2p) != "1.11"

возвращает: 0 так как 1.10.1 ниже 1.11

можно использовать версия CLI для проверки ограничений версии

$ version ">=1.0, <2.0" "1.7"
$ go version | version ">=1.9"

пример скрипта Bash:

#!/bin/bash

if `version -b ">=9.0.0" "$(gcc --version)"`; then
  echo "gcc version satisfies constraints >=9.0.0"
else
  echo "gcc version doesn't satisfies constraints >=9.0.0"
fi

вот чистое решение Bash, которое поддерживает ревизии (например, '1.0-r1'), основанные на ответ отправлен Деннисом Уильямсоном. Он может быть легко изменен для поддержки таких вещей, как'- RC1 ' или извлечь версию из более сложной строки, изменив регулярное выражение.

для получения подробной информации о реализации, пожалуйста, обратитесь к комментариям в коде и / или включите включенный код отладки:

#!/bin/bash

# Compare two version strings [: version string 1 (v1), : version string 2 (v2)]
# Return values:
#   0: v1 == v2
#   1: v1 > v2
#   2: v1 < v2
# Based on: https://stackoverflow.com/a/4025065 by Dennis Williamson
function compare_versions() {

    # Trivial v1 == v2 test based on string comparison
    [[ "" == "" ]] && return 0

    # Local variables
    local regex="^(.*)-r([0-9]*)$" va1=() vr1=0 va2=() vr2=0 len i IFS="."

    # Split version strings into arrays, extract trailing revisions
    if [[ "" =~ ${regex} ]]; then
        va1=(${BASH_REMATCH[1]})
        [[ -n "${BASH_REMATCH[2]}" ]] && vr1=${BASH_REMATCH[2]}
    else
        va1=()
    fi
    if [[ "" =~ ${regex} ]]; then
        va2=(${BASH_REMATCH[1]})
        [[ -n "${BASH_REMATCH[2]}" ]] && vr2=${BASH_REMATCH[2]}
    else
        va2=()
    fi

    # Bring va1 and va2 to same length by filling empty fields with zeros
    (( ${#va1[@]} > ${#va2[@]} )) && len=${#va1[@]} || len=${#va2[@]}
    for ((i=0; i < len; ++i)); do
        [[ -z "${va1[i]}" ]] && va1[i]="0"
        [[ -z "${va2[i]}" ]] && va2[i]="0"
    done

    # Append revisions, increment length
    va1+=($vr1)
    va2+=($vr2)
    len=$((len+1))

    # *** DEBUG ***
    #echo "TEST: '${va1[@]} (?) ${va2[@]}'"

    # Compare version elements, check if v1 > v2 or v1 < v2
    for ((i=0; i < len; ++i)); do
        if (( 10#${va1[i]} > 10#${va2[i]} )); then
            return 1
        elif (( 10#${va1[i]} < 10#${va2[i]} )); then
            return 2
        fi
    done

    # All elements are equal, thus v1 == v2
    return 0
}

# Test compare_versions [: version string 1, : version string 2, : expected result]
function test_compare_versions() {
    local op
    compare_versions "" ""
    case $? in
        0) op="==" ;;
        1) op=">" ;;
        2) op="<" ;;
    esac
    if [[ "$op" == "" ]]; then
        echo -e "\e[1;32mPASS: ' $op '\e[0m"
    else
        echo -e "\e[1;31mFAIL: '  ' (result: ' $op ')\e[0m"
    fi
}

echo -e "\nThe following tests should pass:"
while read -r test; do
    test_compare_versions $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         ==
1.0-r1       1.0-r3       <
1.0-r9       2.0          <
3.0-r15      3.0-r9       >
...-r1       ...-r2       <
2.0-r1       1.9.8.21-r2  >
1.0          3.8.9.32-r   <
-r           -r3          <
-r3          -r           >
-r3          -r3          ==
-r           -r           ==
0.0-r2       0.0.0.0-r2   ==
1.0.0.0-r2   1.0-r2       ==
0.0.0.1-r7   -r9          >
0.0-r0       0            ==
1.002.0-r6   1.2.0-r7     <
001.001-r2   1.1-r2       ==
5.6.1-r0     5.6.1        ==
EOF

echo -e "\nThe following tests should fail:"
while read -r test; do
    test_compare_versions $test
done << EOF
1            1            >
3.0.5-r5     3..5-r5      >
4.9.21-r3    4.8.22-r9    <
1.0-r        1.0-r1       ==
-r           1.0-r        >
-r1          0.0-r1       <
-r2          0-r2         <
EOF

echo -e "\nThe following line should be empty (local variables test):"
echo "$op $regex $va1 $vr1 $va2 $vr2 $len $i $IFS"