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

12

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

$ bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

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

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

WinEunuuchs2Unix
источник
Вы смотрели на $BASH_VERSIONи $BASH_VERSINFOпеременные?
Steeldriver
@steeldriver Нет. Это было бы приемлемым ответом в конкретном случае Bash, если вы хотите опубликовать его. Однако для других случаев переменные среды могут быть недоступны.
WinEunuuchs2Unix
Кстати, по теме: askubuntu.com/questions/39309/…
Сергей Колодяжный
@SergiyKolodyazhnyy, который связан только с bash. Хотя я ищу однострочник bash или скрипт / функцию, проверка только номера версии bash не является конечной целью. --versionПервоначальным намерением была общая процедура проверки всех программ путем добавления и проверки выходных данных. Я отредактировал вопрос соответственно.
WinEunuuchs2Unix
Форматы номеров версий сильно различаются в разных программах. То, как они сообщают подробности версии, также сильно различается. Просто не практично просить способ проверить версию случайной программы. Этот вопрос слишком широк.
Муру

Ответы:

16

Пытаться:

$ [ "${BASH_VERSINFO:-0}" -ge 4 ] && echo "bash supports associative arrays"
bash supports associative arrays

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 -c 'import platform; print(platform.python_version())'
2.7.13

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

$ uname -r
4.9.0-2-amd64
John1024
источник
Отличные ответы! Просто обратите внимание, что у вас uname -r"4.9.0-2-amd64", который может тестировать больше, чем мой "4.11.1-041101-generic" с помощью обычного теста bash, когда на самом деле номер моей версии больше.
WinEunuuchs2Unix
2
@ WinEunuuchs2Unix Инструменты Python могут точно сравнивать строки версий. См. Сравнение версий строк в Python
wjandrea
Джон - пример с питоном может быть сокращен с помощью $ python --versionкоторого возвращается Python 2.7.12. @ wjandrea-- спасибо за ссылку +1. Возможно, я мог бы составить таблицу всех названных имен программ и минимальных номеров версий. Затем передайте таблицу измененной копии предоставленной pythonвами ссылки. Поскольку grubвы можете вызывать только скомпилированный Python, вы можете подумать, что для этого существует двоичный файл или это возможно в оболочке.
WinEunuuchs2Unix
9

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

unset assoc
if ! declare -A assoc ; then
    echo "associative arrays not supported!"
    exit 1
fi

( declare -A varтакже не работает, если varмассив неассоциативен, поэтому unsetсначала.)

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


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

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

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

if dpkg --compare-versions 4.3.30 ge 4.0.0 ; then
    echo "it's version 4.x"
fi

Или просто объединить вышеперечисленное:

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' in your path is version 4.x"
fi
ilkkachu
источник
5

Есть несколько способов приблизиться к тому, чего вы хотите достичь.

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

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

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

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

Альтернативный способ - через оператор case или if. Лично я бы сделал это:

bash-4.3$ case $BASH_VERSION in 4.*) echo "Can use associative arrays";; ?) echo "can't use associative arrays" ;; esac
Can use associative arrays

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

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

#!/bin/bash
version_above_4(){
    # check if $BASH_VERSION is set at all
    [ -z $BASH_VERSION ] && return 1

    # If it's set, check the version
    case $BASH_VERSION in 
        4.*) return 0 ;;
        ?) return 1;; 
    esac
}

if version_above_4
then
    echo "Good"
else
    echo "No good"
fi

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

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

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

$ apt-cache policy bash | awk -F '[:.]' '/Installed:/{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.

Сергей Колодяжный
источник
Ассоциативный массив является вторым из «двух массивов», как вы предлагаете в разделе 4. Его единственная цель - удерживать ключ «путь / к / имя-файла», а значение - это индекс в основном массиве. Ассоциативный массив был создан для ускорения поиска в обычном индексированном массиве, который в настоящее время является последовательным. Несмотря на то, что я в любом случае присматриваюсь к Python, моя текущая совместимость с bash сфокусирована на Ubuntu и, возможно, Bash для Windows 10. Мне нравится ваш скрипт, который можно адаптировать. Например, yad --versionвозвращает, 0.37.0 (GTK+ 3.18.9)но новые функции в настоящее время 0.39.
WinEunuuchs2Unix
отличный и полезный ответ. Спасибо!
qodeninja
2

Однострочно невозможно, но возможен сценарий bash

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

Код

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

#!/bin/bash

# NAME: testver
# PATH: /usr/local/bin
# DESC: Test a program's version number >= to passed version number
# DATE: May 21, 2017. Modified August 5, 2019.

# CALL: testver Program Version

# PARM: 1. Program - validated to be a command
#       2. Version - validated to be numberic

# NOTE: Extracting version number perl one-liner found here:
#       http://stackoverflow.com/questions/16817646/extract-version-number-from-a-string

#       Comparing two version numbers code found here:
#       http://stackoverflow.com/questions/4023830/how-compare-two-strings-in-dot-separated-version-format-in-bash

# Map parameters to coder-friendly names.
Program="$1"
Version="$2"

# Program name must be a valid command.
command -v $Program >/dev/null 2>&1 || { echo "Command: $Program not found. Check spelling."; exit 99; }

# Passed version number must be valid format.
if ! [[ $Version =~ ^([0-9]+\.?)+$ ]]; then
    echo "Version number: $Version has invalid format. Aborting.";
    exit 99
fi

InstalledVersion=$( "$Program" --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/' )
# echo "Installed Version: $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 "Installed ${l[$i]} -gt Test ${r[$i]}?"
        [[ ${l[$i]} -gt ${r[$i]} ]] && exit 0 # Installed version > test version.
        [[ ${l[$i]} -lt ${r[$i]} ]] && exit 1 # Installed version < test version.
    done

    exit 0 # Installed version = test version.
else
    echo "Invalid version number found for command: $Program"
    exit 99
fi

echo "testver - Unreachable code has been reached!"
exit 255
WinEunuuchs2Unix
источник