Bash однострочник, чтобы проверить, является ли версия>=
Я ищу лучшую однострочную версию 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 я бы хотел провести однострочный тест самым элегантным / эффективным / читабельным способом.
Я ожидаю много ответов для достижения цели, так что за эту работу с благодарностью проголосуют.
Хотя этот вопрос относится к номеру версии bash, с модификацией ответы могут относиться к другим приложениям, таким как LibreOffice, Python, systemd, kernel и т. Д., Которые могут предоставлять различные функции в зависимости от номера версии.
Я добавляю дополнительный пример, потому что может быть много программ, которые вы можете вызывать в своем приложении, и некоторые из них должны быть в определенной версии.
Вот пример из реальной жизни:
$ yad --version
0.37.0 (GTK+ 3.18.9)
YAD (еще одно диалоговое окно) выпустила версию 0.39
27 апреля 2017 года, и он еще не попал в репозитории Ubuntu. Чтобы использовать новые функции, мне пришлось бы вручную загружать и компилировать из источника. Если я сделаю это, тест будет >= 0.39
,
В дальнейшем --version
примеры из тестирования (отображается только первая строка):
$ grep --version
grep (GNU grep) 2.25
$ /bin/dd --version
dd (coreutils) 8.25
$ /bin/rm --version
rm (GNU coreutils) 8.25
$ ls --version
ls (GNU coreutils) 8.25
Хорошие ответы были опубликованы ТОЛЬКО для проверки BASH-версии. Я надеюсь на надежный скрипт, который работает не только для bash. Возможно, "--version" может быть добавлен после имени программы в скрипте? Номер версии сравнивается правильно? то есть. Простой строковый тест "4.9.10 > 4.11.1", вероятно, возвращает true в bash, но это должно быть false.
Сравнение номеров версий с десятичными разделителями
Существует много способов сравнения двух номеров версий, содержащих десятичные разделители, в ответ на переполнение стека. Вопрос и принятый ответ были опубликованы в 2010 году, но я склоняюсь к ответу, опубликованному в декабре 2016 года.
4 ответа
Пытаться:
$ [ "${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
Вместо сравнения номеров версий вы можете проверить непосредственно саму функцию. 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
Есть несколько способов приблизиться к тому, чего вы хотите достичь.
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 "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
,
Однострочное невозможно, но возможен сценарий bash
Я разработал скрипт, который опирается на ответы в Stack Overflow. Один из этих ответов привел к тому, что сотрудник Dell написал в 2004 году сравнение номеров версий для приложения DKMS.
Первоначальный код сравнения номеров версий, который я упоминал в своем вопросе, не работал (по крайней мере, для меня).
Пример выходов:
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ testver perl 5.22.1
11= greater, 10= same and 9= less: 10
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ testver perl 5.22.3
11= greater, 10= same and 9= less: 9
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ testver perl 5.22.0
11= greater, 10= same and 9= less: 11
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ testver crazy 5.22.0
Command: crazy not found. Check spelling.
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ testver perl 5..22.0
Version number: 5..22.0 has invalid format. Aborting.
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ testver yad 0.39
11= greater, 10= same and 9= less: 9
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$ testver yad 0.37
11= greater, 10= same and 9= less: 10
───────────────────────────────────────────────────────────────────────────────
rick@dell:~$
Код
Сценарий 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.
# CALL: testver Program Version
# PARM: 1. Program - validated to be a command
# 2. Version - validated to be numeric
# NOTE: Extracting version number perl one-liner found here:
# http://stackoverflow.com/questions/16817646/extract-version-number-from-a-string
# Comparing two version numbers written by Dell employee for DKMS application:
# http://lists.us.dell.com/pipermail/dkms-devel/2004-July/000142.html
# 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
# Get current version number of installed program
InstalledVersion=$( "$Program" --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/' )
# Sanity check
if ! [[ $InstalledVersion =~ ^([0-9]+\.?)+$ ]]; then
echo "Invalid version number: $InstalledVersion found for command: $Program"
exit 99
fi
version_checker() {
local ver1=$InstalledVersion
while [ `echo $ver1 | egrep -c [^0123456789.]` -gt 0 ]; do
char=`echo $ver1 | sed 's/.*\([^0123456789.]\).*/\1/'`
char_dec=`echo -n "$char" | od -b | head -1 | awk {'print $2'}`
ver1=`echo $ver1 | sed "s/$char/.$char_dec/g"`
done
local ver2=$Version
while [ `echo $ver2 | egrep -c [^0123456789.]` -gt 0 ]; do
char=`echo $ver2 | sed 's/.*\([^0123456789.]\).*/\1/'`
char_dec=`echo -n "$char" | od -b | head -1 | awk {'print $2'}`
ver2=`echo $ver2 | sed "s/$char/.$char_dec/g"`
done
ver1=`echo $ver1 | sed 's/\.\./.0/g'`
ver2=`echo $ver2 | sed 's/\.\./.0/g'`
do_version_check "$ver1" "$ver2"
}
do_version_check() {
[ "$1" == "$2" ] && return 10
ver1front=`echo $1 | cut -d "." -f -1`
ver1back=`echo $1 | cut -d "." -f 2-`
ver2front=`echo $2 | cut -d "." -f -1`
ver2back=`echo $2 | cut -d "." -f 2-`
if [ "$ver1front" != "$1" ] || [ "$ver2front" != "$2" ]; then
[ "$ver1front" -gt "$ver2front" ] && return 11
[ "$ver1front" -lt "$ver2front" ] && return 9
[ "$ver1front" == "$1" ] || [ -z "$ver1back" ] && ver1back=0
[ "$ver2front" == "$2" ] || [ -z "$ver2back" ] && ver2back=0
do_version_check "$ver1back" "$ver2back"
return $?
else
[ "$1" -gt "$2" ] && return 11 || return 9
fi
}
version_checker "$InstalledVersion" "$Version"
TestResults=$?
echo "11= greater, 10= same and 9= less: $TestResults"
[[ $TestResults -eq 9 ]] && exit 1 ;
exit 0
Этот код не так чист, как хотелось бы, и отражает тот факт, что части написаны тремя разными людьми.