Проверьте, если версия Bash >= заданного номера версии

Вопрос или проблема

Мне нужно проверить, если номер версии Bash >= определенному числу. Например, у меня есть:

$ bash --version
GNU bash, версия 4.3.48(1)-release (x86_64-pc-linux-gnu)
Авторские права (C) 2013 Фонд свободного программного обеспечения, Inc.
Лицензия GPLv3+: GNU GPL версия 3 или более поздняя <http://gnu.org/licenses/gpl.html>

Это бесплатное программное обеспечение; вы можете изменять и распространять его.
Гарантий НЕТ, в той степени, в какой это разрешено законом.

Для использования ассоциативных массивов номер версии bash должен быть >=4.

В моем bash-скрипте я хотел бы включить однострочную проверку наиболее элегантным / эффективным / читаемым способом, но также принимаются и другие подходы.

Попробуйте:

$ [ "${BASH_VERSINFO:-0}" -ge 4 ] && echo "bash поддерживает ассоциативные массивы"
bash поддерживает ассоциативные массивы

BASH_VERSINFO — это переменная массива только для чтения, члены которой содержат информацию о версии для этого экземпляра bash. Поскольку она была введена с bash 2.0, скорее всего, она поддерживается всеми версиями bash, с которыми вы столкнетесь. Но, чтобы быть осторожным, мы включаем значение по умолчанию 0 для любой более ранней версии bash, для которой эта переменная не задана.

Извлечение информации о версии из других программ

Вы спрашивали о LibreOffice, Python, ядре и т. д.

LibreOffice выдает информацию о версии, которая выглядит так:

$ libreoffice --version
LibreOffice 5.2.6.2 20m0(Build:2)

Чтобы извлечь номер версии:

$ libreoffice --version | cut -d' ' -f2
5.2.6.2

Для python:

$ python -c 'import platform; print(platform.python_version())'
2.7.13

Чтобы получить версию ядра, используйте uname:

$ uname -r
4.9.0-2-amd64

Вместо сравнения номеров версий вы можете напрямую проверить наличие самой функции. declare -A возвращает 2 (по крайней мере, в Bash 3.2), если он не распознает -A, поэтому проверьте это (также выводится ошибка, так что, возможно, стоит от нее избавиться):

if ! declare -A assoc 2>/dev/null; then
    echo "ассоциативные массивы не поддерживаются!"
    exit 1
fi

(declare -A var также не сработает, если var является неассоциативным массивом, но это может произойти только если скрипт сделал это заранее.)

Хотя я действительно не предполагаю, что кто-то будет переносить функции в Bash, в общем, более уместно проверять функции, а не версии.
Даже в случае Bash кто-то может скомпилировать версию с ограниченным набором функций…


Более общий случай тестирования номеров версий имеет две части: 1) как найти правильный номер версии для тестирования и 2) как сравнить его с другим значением.

Первый более трудный. Многие программы сообщают свой номер версии с помощью флага командной строки, например --version или -v, но формат вывода варьируется, и программно выбирать номер версии может быть сложно. Затем возникает вопрос о возможности наличия нескольких версий одной и той же программы, установленных одновременно.

Второе зависит от некоторых знаний о формате номеров версии. dpkg может сравнивать номера версий в стиле Debian (которые, как мне кажется, включают версии типа semver в качестве подмножества):

if dpkg --compare-versions 4.3.30 ge 4.0.0 ; then
    echo "это версия 4.x"
fi

Или, просто чтобы объединить вышеизложенное, используя sed, чтобы получить только числовой номер версии из вывода:

bashver=$( bash --version | sed -Ee 's/GNU bash, version ([0-9.]+).*/\1/;q' )
if dpkg --compare-versions "$bashver" ge 4.0.0 ; then
    echo "'bash' в вашем пути — версия 4.x или новее"
fi

Хотя в Bash вы также можете использовать массив $BASH_VERSINFO, который дает разложенное представление номера версии. ${BASH_VERSINFO[0]} будет содержать основную версию, так что этого должно быть достаточно для проверки:

if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
    echo "работающая оболочка старше Bash 4.0 (или не Bash вообще)"
    exit 1
fi

Существует несколько способов подойти к тому, что вы хотите достичь.

1. Используйте $BASH_VERSION

Достаточно просто посмотреть, что в переменной $BASH_VERSION. Лично я бы использовал подсмотренный процесс так:

$ (read -d "." version trash <<< $BASH_VERSION; echo "$version" )
4

Обратите внимание, что синтаксис <<< для here-doc не является переносимым, если вы собираетесь использовать его с /bin/sh, который является Dash в Ubuntu и может быть чем-то другим на другой системе.

Другой способ — через оператор case или оператор if. Лично я бы сделал так:

bash-4.3$ case $BASH_VERSION in 4.*) echo "Можно использовать ассоциативные массивы";; ?) echo "нельзя использовать ассоциативные массивы" ;; esac
Можно использовать ассоциативные массивы

Вероятно, ради переносимости вы должны сначала проверить, задана ли такая переменная вообще, с помощью чего-то вроде [ -n $BASH_VERSION ]

Это все вполне может быть переписано как функция, используемая в скрипте. Что-то вроде:

#!/bin/bash
version_above_4(){
    # проверьте, задана ли $BASH_VERSION
    [ -z $BASH_VERSION ] && return 1
   
    # Если она задана, проверьте версию
    case $BASH_VERSION in 
        4.*) return 0 ;;
        ?) return 1;; 
    esac
}

if version_above_4
then
    echo "Хорошо"
else
    echo "Плохо"
fi

Это не однострочник, хотя это гораздо лучше. Качество важнее количества.

2. Проверьте, что установлено

Для этого вам нужно отфильтровать вывод apt-cache policy так:

$ apt-cache policy bash | awk -F '[:.]' '/Установлено:/{printf "%s\n",substr($2,2)}'
4

dpkg-query также может быть полезен с некоторой фильтрацией через awk.

$ dpkg-query -W bash | awk '{print substr($2,1,1)}'   
4

Обратите внимание, что это не переносимо, так как если на системе нет dpkg или apt, (например, RHEL или FreeBSD), это не принесет вам никакой пользы.

3. Используйте set -e, чтобы выйти из скрипта, если произошла ошибка

Один из способов обойти это — просто продолжить и использовать ассоциативные массивы и выйти, когда bash не может их использовать. Строка set -e ниже #!/bin/bash позволит скрипту завершиться, если скрипт не может использовать ассоциативный массив.

Это потребует явно сообщить пользователю: “Эй, тебе действительно нужна версия bash 4.3 или новее, иначе скрипт не сработает”. Тогда ответственность ложится на пользователя, хотя некоторые могут утверждать, что это не лучший подход к разработке программного обеспечения.

4. Оставьте все надежды и пишите переносимые скрипты, совместимые с POSIX

bash скрипты не переносимы, потому что их синтаксис не совместим с оболочкой Bourne. Если скрипт, который вы пишете, будет использоваться на разных системах, а не только на Ubuntu, то оставьте все надежды и найдите способы использовать что-то кроме ассоциативных массивов. Это может включать наличие двух массивов или разбор конфигурационного файла. Также рассмотрите возможность перехода на другой язык, Perl или Python, где синтаксис по крайней мере более переносим, чем bash.

Однострочник невозможен, но bash-скрипт возможен

Я разработал скрипт, основываясь на ответах на Stack Overflow. Один из этих ответов привел к тому, что сотрудник Dell написал сравнения номеров версий в 2004 году для приложения DKMS.

Код

Ниже представленный bash-скрипт должен быть помечен как исполняемый с помощью команды chmod a+x script-name. Я использую имя /usr/local/bin/testver:

#!/bin/bash

# ИМЯ: testver
# ПУТЬ: /usr/local/bin
# ОПИСАНИЕ: Тестирует, больше ли номер версии программы, чем переданный номер версии
# ДАТА: 21 мая 2017 года. Изменен 5 августа 2019 года.

# ВЫЗОВ: testver Программа Версия

# ПАРАМЕТР: 1. Программа - проверяется, чтобы быть командой
#           2. Версия - проверяется, чтобы быть числом

# ЗАМЕТКА: Извлечение номера версии однострочник perl найден здесь:
#          http://stackoverflow.com/questions/16817646/extract-version-number-from-a-string

#          Сравнение двух номеров версий код найден здесь:
#          http://stackoverflow.com/questions/4023830/how-compare-two-strings-in-dot-separated-version-format-in-bash

# Сопоставьте параметры с удобными для кодирования именами.
Program="$1"
Version="$2"

# Имя программы должно быть действительной командой.
command -v $Program >/dev/null 2>&1 || { echo "Команда: $Program не найдена. Проверьте правописание."; exit 99; }

# Переданный номер версии должен быть допустимым форматом.
if ! [[ $Version =~ ^([0-9]+\.?)+$ ]]; then
    echo "Номер версии: $Version имеет недопустимый формат. Прерывание.";
    exit 99
fi

InstalledVersion=$( "$Program" --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/' )
# echo "Установленная версия: $InstalledVersion"

if [[ $InstalledVersion =~ ^([0-9]+\.?)+$ ]]; then
    l=(${InstalledVersion//./ })
    r=(${Version//./ })
    s=${#l[@]}
    [[ ${#r[@]} -gt ${#l[@]} ]] && s=${#r[@]}

    for i in $(seq 0 $((s - 1))); do
        # echo "Установленная ${l[$i]} -gt Тест ${r[$i]}?"
        [[ ${l[$i]} -gt ${r[$i]} ]] && exit 0 # Установленная версия > тестовая версия.
        [[ ${l[$i]} -lt ${r[$i]} ]] && exit 1 # Установленная версия < тестовая версия.
    done

    exit 0 # Установленная версия = тестовая версия.
else
    echo "Недопустимый номер версии найден для команды: $Program"
    exit 99
fi

echo "testver - Доступен недостижимый код!"
exit 255

Ответ или решение

Чтобы проверить, соответствует ли версия Bash заданному номеру (например, >= 4.0 для использования ассоциативных массивов), мы можем использовать ряд подходов. Ниже приведены подробные шаги и примеры кода на Bash для выполнения этой операции.

Использование переменной BASH_VERSINFO

Переменная BASH_VERSINFO представляет собой массив, который хранит информацию о версии текущей Bash. Это один из наиболее простых и надежных способов проверки версии.

if [ "${BASH_VERSINFO[0]}" -ge 4 ]; then
    echo "Bash поддерживает ассоциативные массивы."
else
    echo "Версия Bash устарела для поддержки ассоциативных массивов."
    exit 1
fi

Альтернативный подход с использованием BASH_VERSION

Если вы хотите использовать переменную BASH_VERSION, вы можете извлечь главную версию следующим образом:

main_version=${BASH_VERSION%%.*}
if [ "$main_version" -ge 4 ]; then
    echo "Bash поддерживает ассоциативные массивы."
else
    echo "Версия Bash устарела для поддержки ассоциативных массивов."
    exit 1
fi

Проверка установленной версии с использованием dpkg

Если вы хотите проверить версию Bash, установленную на вашем компьютере, можно использовать команду dpkg для получения версии:

installed_version=$(dpkg-query -W -f='${Version}' bash | cut -d'.' -f1)
if [ "$installed_version" -ge 4 ]; then
    echo "Установленная версия Bash >= 4."
else
    echo "Установленная версия Bash < 4."
    exit 1
fi

Проверка с помощью запроса к командной строке

Также вы можете проверить версию Bash, запустив команду bash --version, и используя утилиты обработки текста, такие как awk или sed:

bash_version=$(bash --version | awk '{print $4}' | cut -d. -f1) 
if [ "$bash_version" -ge 4 ]; then
    echo "Bash поддерживает ассоциативные массивы."
else
    echo "Версия Bash устарела для поддержки ассоциативных массивов."
    exit 1
fi

Проверка на поддержку функции

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

if ! declare -A test_array &> /dev/null; then
    echo "Ассоциативные массивы не поддерживаются!"
    exit 1
else
    echo "Ассоциативные массивы поддерживаются!"
fi

Заключение

Выбор метода проверки версии Bash зависит от ваших требований и предпочтений. Лучше всегда проверять наличие необходимых функций, так как можно столкнуться с использованием кастомных сборок Bash или сред, которые могут иметь разные версии с ограниченной функциональностью. Ваш скрипт должен легко адаптироваться к изменениям в версии Bash и предлагать пользователям четкие инструкции о рекомендуемой версии.

Эти примеры должны помочь вам эффективно проверить версию Bash и обрабатывать случаи, когда версия не соответствует требованиям.

Оцените материал
Добавить комментарий

Капча загружается...