Как сравнить две строки в формате dot separated version в Bash?
есть ли способ сравнить такие строки на bash, например:2.4.5
и 2.8
и 2.4.5.1
?
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'
вероятно, нет универсального правильного способа достичь этого. Если вы пытаетесь сравнить версии в системе пакетов 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
вы можете рекурсивно разделить на
.
и сравнить, как показано в следующем алгоритме, взяты из здесь. Он возвращает 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
нет (а BusyBoxexpr 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"