- Вопрос или проблема
- Извлечение информации о версии из других программ
- 1. Используйте $BASH_VERSION
- 2. Проверьте, что установлено
- 3. Используйте set -e, чтобы выйти из скрипта, если произошла ошибка
- 4. Оставьте все надежды и пишите переносимые скрипты, совместимые с POSIX
- Однострочник невозможен, но bash-скрипт возможен
- Код
- Ответ или решение
- Использование переменной BASH_VERSINFO
- Альтернативный подход с использованием BASH_VERSION
- Проверка установленной версии с использованием dpkg
- Проверка с помощью запроса к командной строке
- Проверка на поддержку функции
- Заключение
Вопрос или проблема
Мне нужно проверить, если номер версии 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 и обрабатывать случаи, когда версия не соответствует требованиям.