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

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

Мне нужно проверить, является ли номер версии 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-скрипте я хотел бы реализовать однострочный тест наиболее элегантным, эффективным и читабельным способом, но допускаются и другие подходы.

решение1

Пытаться:

$ [ "${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

решение2

Вместо сравнения номеров версий вы можете проверить непосредственно саму функцию. 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(что, я думаю, включает в себясемверверсии типа как подмножество):

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

решение3

Есть несколько способов приблизиться к желаемому результату.

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

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

$ (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 "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

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

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

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

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

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

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

решение4

Однострочный вариант невозможен, но возможен скрипт 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

Связанный контент