
Мне нужно проверить, является ли номер версии 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