Подтвердить что ты не робот

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
4b9b3361

Ответ 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 имеет определенные достоинства:

  1. Нет вызовов внешним утилитам, таким как sort, awk, gawk, tr и т.д.

  2. Обрабатывает числовые версии произвольного размера (до предела оболочки для целочисленных вычислений)

  3. Обрабатывает произвольное количество "пунктирных" частей.

Ответ 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 в середине.