BASH сравнение номеров версий
Привет, ребята, у меня есть этот script, который должен убедиться, что текущая PHP-версия пользователей находится между определенным диапазоном, хотя она ДОЛЖНА работать, есть ошибка где-то, что заставляет думать, что версия вне диапазона, может кто-то взглянет и скажет мне, что я могу сделать, чтобы исправить это?
function version { echo "[email protected]" | gawk -F. '{ printf("%d.%d.%d\n", $1,$2,$3); }'; }
phpver=`php -v |grep -Eow '^PHP [^ ]+' |gawk '{ print $2 }'`
if [ $(version $phpver) > $(version 5.2.13) ] || [ $(version $phpver) < $(version 5.2.13) ]; then
echo "PHP Version $phpver must be between 5.2.13 - 5.3.15"
exit
fi
Ответы
Ответ 1
Здесь, как сравнивать версии.
с помощью sort -V
:
function version_gt() { test "$(printf '%s\n' "[email protected]" | sort -V | head -n 1)" != "$1"; }
пример использования:
first_version=5.100.2
second_version=5.1.2
if version_gt $first_version $second_version; then
echo "$first_version is greater than $second_version !"
fi
про:
- надежный способ сравнения строк фантазийной версии:
- поддерживать любую длину подчастей (например: 1.3alpha.2.dev2 > 1.1?)
- поддерживает альфа-бетую сортировку (то есть: 1.alpha < 1.beta2)
- поддержка большого размера версии (т.е.: 1.10003939209329320932 > 1.2039209378273789273?)
- может быть легко изменен для поддержки n аргументов. (выведенный как упражнение;))
- обычно очень полезно с тремя аргументами: (т.е.: 1.2 < my_version < 2.7)
минусы:
- использует множество различных вызовов для разных программ. Так что это не так эффективно.
- использует довольно недавнюю версию
sort
, и она может быть недоступна на вашем
система. (проверьте с помощью man sort
)
без sort -V
:
## each separate version number must be less than 3 digit wide !
function version { echo "[email protected]" | gawk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'; }
пример использования:
first_version=5.100.2
second_version=5.1.2
if [ "$(version "$first_version")" -gt "$(version "$second_version")" ]; then
echo "$first_version is greater than $second_version !"
fi
про:
- более быстрое решение, поскольку оно вызывает только один подпроцесс
- гораздо более совместимое решение.
минусы:
- довольно специфично, строка версии должна:
- имеют версию только с 1, 2 или 3 частями. (исключая "2.1.3.1" )
- каждая часть должна быть только численной (исключая "3.1a" )
- каждая часть не может быть больше 999 (исключая "1.20140417" )
Комментарии о вашем script:
Я не вижу, как это может работать:
- как указано в комментарии
>
и <
, являются очень специальным символом оболочки, и вы должны заменить их на -gt
и -lt
- даже если вы заменили символы, вы не можете сравнивать номера версий, как если бы они были целыми или плавают. Например, в моей системе версия php
5.5.9-1ubuntu4
.
Но ваша функция version()
уже достаточно хорошо написана и может помочь вам, обойдя классическую проблему, что сортировка по алфавиту чисел не будет сортировать числа численно (в алфавитном порядке 1 и lt; 11 и 2, что является неправильным численно). Но будьте осторожны: произвольно большие числа не поддерживаются bash (старайтесь держать под 32 бит, если вы нацелены на совместимость с 32-битными системами, так что это будет 9-значный длинный номер). Поэтому я изменил код (во втором методе НЕ используя sort -V
), чтобы набрать только 3 цифры для каждой части строки версии.
EDIT: применяется @phk amelioration, так как оно заметно умнее и удаляет вызов подпроцесса в первой версии с помощью sort
. Спасибо.
Ответ 2
Есть возможность для deb-дистрибутивов:
dpkg --compare-versions <version> <relation> <version>
Например:
dpkg --compare-versions "0.0.4" "gt" "0.0.3"
if [ $? -eq "0" ]; then echo "YES"; else echo "NO"; fi
Ответ 3
Делает лексическое сравнение. Используйте один из следующих вариантов:
if [ $(version $phpver) -gt $(version 5.2.13) ] || [ $(version $phpver) -lt $(version 5.2.13) ]; then
if [[ $(version $phpver) > $(version 5.2.13) ]] || [[ $(version $phpver) < $(version 5.2.13) ]]; then
if (( $(version $phpver) > $(version 5.2.13) )) || (( $(version $phpver) < $(version 5.2.13) )); then
Или сделайте все это в awk или другом инструменте. Это кричит для некоторой оптимизации. Кажется, вы тоже не выпускаете номера, поэтому у вас довольно странный дизайн. Обычно подстроки версии умножаются на 1000, а затем все суммируются, чтобы получить один сопоставимый скаляр.
Ответ 4
Здесь другое решение, которое:
- не выполняет никакой внешней команды кроме
tr
- не имеет ограничений на количество частей в строке версии
- может сравнивать строки версий с различным количеством частей.
Обратите внимание, что это Bash код с использованием переменных массива.
compare_versions()
{
local v1=( $(echo "$1" | tr '.' ' ') )
local v2=( $(echo "$2" | tr '.' ' ') )
local len="$(max "${#v1[*]}" "${#v2[*]}")"
for ((i=0; i<len; i++))
do
[ "${v1[i]:-0}" -gt "${v2[i]:-0}" ] && return 1
[ "${v1[i]:-0}" -lt "${v2[i]:-0}" ] && return 2
done
return 0
}
Функция возвращает:
- 0, если версии равны (btw: 1.2 == 1.2.0)
- 1, если 1-я версия больше/более новая
- 2, если вторая версия больше/более новая
Однако # 1 - для этого требуется еще одна функция (но функция min
вполне может быть использована):
min()
{
local m="$1"
for n in "[email protected]"
do
[ "$n" -lt "$m" ] && m="$n"
done
echo "$m"
}
Однако # 2 - он не может сравнивать строки версий с альфа-числовыми частями (хотя это не составит труда добавить на самом деле).
Ответ 5
Следующее решение должно более точно отражать ваши конкретные потребности. Его можно использовать для проверки того, находится ли строка версии CURRENT
между MIN
и MAX
. Я предполагаю, что MIN
и MAX
являются допустимыми номерами версий (т.е. MIN <= CURRENT <= MAX
, а не MIN < CURRENT < MAX
).
# Usage: version MIN CURRENT MAX
version(){
local h t v
[[ $2 = "$1" || $2 = "$3" ]] && return 0
v=$(printf '%s\n' "[email protected]" | sort -V)
h=$(head -n1 <<<"$v")
t=$(tail -n1 <<<"$v")
[[ $2 != "$h" && $2 != "$t" ]]
}
Например...
if ! version 5.2.13 "$phpver" 5.3.15; then
echo "PHP Version $phpver must be between 5.2.13 - 5.3.15"
exit
fi
Ответ 6
Если вы находитесь на Bash 3 со старой версией sort
(посмотрите на macOS...), я создал следующий вспомогательный script, который вы можете использовать в качестве источника (также может запускаться как команды):
https://github.com/unicorn-fail/version_compare
Ответ 7
Более безопасный вариант для тестирования версии CLI для PHP - это использование собственной функции version_compare
PHP.
#!/bin/bash
MIN_VERSION="7.0.0"
MAX_VERSION="7.1.4"
PHP_VERSION=`php -r 'echo PHP_VERSION;'`
function version_compare() {
COMPARE_OP=$1;
TEST_VERSION=$2;
RESULT=$(php -r 'echo version_compare(PHP_VERSION, "'${TEST_VERSION}'", "'${COMPARE_OP}'") ? "TRUE" : "";')
test -n "${RESULT}";
}
if ( version_compare "<" "${MIN_VERSION}" || version_compare ">" "${MAX_VERSION}" ); then
echo "PHP Version ${PHP_VERSION} must be between ${MIN_VERSION} - ${MAX_VERSION}";
exit 1;
fi
echo "PHP Version ${PHP_VERSION} is good!";
Ответ 8
Я написал эту неумелую функцию некоторое время назад для аналогичной проблемы. vers_limit вернет 0, если arg1 меньше или равен arg2, в противном случае - не 0:
vers_limit()
{
VERNEW=$1
VERLMT=$2
CHKNEW=$VERNEW
CHKLMT=$VERLMT
PASSED=
while :
do
PARTNEW=${CHKNEW%%.*}
PARTLMT=${CHKLMT%%.*}
if [[ $PARTNEW -lt $PARTLMT ]]
then
PASSED=GOOD
break
elif [[ $PARTNEW -gt $PARTLMT ]]
then
PASSED=
break
else
NXTNEW=${CHKNEW#*.}
if [[ $NXTNEW == $CHKNEW ]]
then
if [[ $NXTNEW == $CHKLMT ]]
then
PASSED=GOOD
break
else
NXTNEW=0
fi
fi
NXTLMT=${CHKLMT#*.}
if [[ $NXTLMT == $CHKLMT ]]
then
NXTLMT=0
fi
fi
CHKNEW=$NXTNEW
CHKLMT=$NXTLMT
done
test "$PASSED"
}
Это не так компактно, как некоторые другие решения здесь, и при этом он не обеспечивает трехсторонний статус (то есть, меньше, равен, больше), хотя я считаю, что можно упорядочить аргументы, интерпретировать состояние прохождения/сбоя и/или позвоните дважды, чтобы достичь желаемого результата. Тем не менее, vers_limit имеет определенные достоинства:
-
Нет вызовов внешним утилитам, таким как sort, awk, gawk, tr и т.д.
-
Обрабатывает числовые версии произвольного размера (до предела оболочки для целочисленных вычислений)
-
Обрабатывает произвольное количество "пунктирных" частей.
Ответ 9
v_min="5.2.15"
v_max="5.3.15"
v_php="$(php -v | head -1 | awk '{print $2}')"
[ ! "$v_php" = "$(echo -e "$v_php\n$v_min\n$v_max" | sort -V | head -2 | tail -1)" ] && {
echo "PHP Version $v_php must be between $v_min - $v_max"
exit
}
Это помещает v_min
, v_max
и v_php
в порядок версий и проверяет, находится ли v_php
в середине.