Я ищу лучший однострочный баш, чтобы проверить, соответствует ли номер версии> = определенному номеру. Например, у меня есть:
$ 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 я хотел бы поставить тест на один лайнер самым элегантным / эффективным / читаемым способом.
Я ожидаю много ответов для достижения цели, поэтому вся эта работа будет с благодарностью -voted.
Хотя этот вопрос специфичен для номера версии 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, но он должен быть ложным.
Существует множество способов сравнения двух номеров версий, содержащих десятичные разделители, как указано в переполнении стека. Вопрос и принятый ответ были опубликованы в 2010 году, но я склоняюсь к ответу, размещенному в декабре 2016 года.
Вместо сравнения номеров версий вы можете проверить непосредственно для самой функции. 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
Попробуйте:
$ [ "${BASH_VERSINFO:-0}" -ge 4 ] && echo "bash supports associative arrays"
bash supports associative arrays
BASH_VERSINFO - переменная массива readonly, члены которой содержат информацию о версии для этого экземпляра 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
Есть несколько способов приблизиться к тому, что вы хотите достичь.
Достаточно просто посмотреть, что находится в переменной $BASH_VERSION. Лично я бы использовал подоболочку следующим образом:
$ (read -d "." version trash <<< $BASH_VERSION; echo "$version" )
4
Обратите внимание, что синтаксис <<< для здесь-doc не переносимый, если вы собираетесь использовать его с /bin/sh, который является Dash on Ubuntu и может быть чем-то другим в другой системе
Альтернативный путь - через case statement или if statement. Лично я сделал бы это:
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
Это не однострочный, хотя это намного лучше. Качество по количеству.
Для этого вам нужно отфильтровать вывод apt-cache policy так, чтобы
$ apt-cache policy bash | awk -F '[:.]' '/Installed:/{printf "%s\n",substr($2,2)}'
4
dpkg-query также может пригодиться с некоторой фильтрацией через awk. [ ! d10] $ dpkg-query -W bash | awk '{print substr($2,1,1)}'
4
Обратите внимание, что это не переносимо, поскольку, если в системе нет (f13) или apt, установленных в системе (например, RHEL или FreeBSD), это не принесет вам пользы. [ ! d11]
. Один из способов обойти это просто просто использовать ассоциативные массивы и выйти, когда bash не может их использовать. Строка set -e ниже #!/bin/bash позволит сценарию выйти, если скрипт не может использовать ассоциативный массив.
Это потребует от вас прямого указания пользователю: «Эй, вам действительно нужна версия bash 4.3 или выше, иначе скрипт не будет работать ». Тогда ответственность лежит на пользователе, хотя некоторые могут утверждать, что это не очень хороший подход к разработке программного обеспечения.
bash, не переносимы, поскольку его синтаксис несовместим с оболочкой Bourne. Если скрипт, который вы пишете, будет использоваться в различных системах, а не только в Ubuntu, то оставите все надежды и найдите способы использования чего-то другого, кроме ассоциативных массивов. Это может включать в себя наличие двух массивов или анализ файла конфигурации. Рассмотрим также переход на другой язык, Perl или Python, где синтаксис, по крайней мере, более переносимый, чем bash.
Я разработал скрипт, который использует ответы в Stack Overflow. Один из этих ответов привел к тому, что в 2004 году для DKMS-приложений были сопоставлены номера версий сотрудников Dell Employee.
Код сравнения исходного кода версии, который я упомянул в моем вопросе, не работал (по крайней мере для меня).
Примеры выходов:
───────────────────────────────────────────────────────────────────────────────
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
Этот код не так чист, как хотелось бы, и отражает то, что части написаны тремя разными людьми.
Вместо сравнения номеров версий вы можете проверить непосредственно для самой функции. 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
Попробуйте:
$ [ "${BASH_VERSINFO:-0}" -ge 4 ] && echo "bash supports associative arrays"
bash supports associative arrays
BASH_VERSINFO - переменная массива readonly, члены которой содержат информацию о версии для этого экземпляра 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
Есть несколько способов приблизиться к тому, что вы хотите достичь.
Достаточно просто посмотреть, что находится в переменной $BASH_VERSION. Лично я бы использовал подоболочку следующим образом:
$ (read -d "." version trash <<< $BASH_VERSION; echo "$version" )
4
Обратите внимание, что синтаксис <<< для здесь-doc не переносимый, если вы собираетесь использовать его с /bin/sh, который является Dash on Ubuntu и может быть чем-то другим в другой системе
Альтернативный путь - через case statement или if statement. Лично я сделал бы это:
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
Это не однострочный, хотя это намного лучше. Качество по количеству.
Для этого вам нужно отфильтровать вывод apt-cache policy так, чтобы
$ apt-cache policy bash | awk -F '[:.]' '/Installed:/{printf "%s\n",substr($2,2)}'
4
dpkg-query также может пригодиться с некоторой фильтрацией через awk. [ ! d10] $ dpkg-query -W bash | awk '{print substr($2,1,1)}'
4
Обратите внимание, что это не переносимо, поскольку, если в системе нет (f13) или apt, установленных в системе (например, RHEL или FreeBSD), это не принесет вам пользы. [ ! d11]
. Один из способов обойти это просто просто использовать ассоциативные массивы и выйти, когда bash не может их использовать. Строка set -e ниже #!/bin/bash позволит сценарию выйти, если скрипт не может использовать ассоциативный массив.
Это потребует от вас прямого указания пользователю: «Эй, вам действительно нужна версия bash 4.3 или выше, иначе скрипт не будет работать ». Тогда ответственность лежит на пользователе, хотя некоторые могут утверждать, что это не очень хороший подход к разработке программного обеспечения.
bash, не переносимы, поскольку его синтаксис несовместим с оболочкой Bourne. Если скрипт, который вы пишете, будет использоваться в различных системах, а не только в Ubuntu, то оставите все надежды и найдите способы использования чего-то другого, кроме ассоциативных массивов. Это может включать в себя наличие двух массивов или анализ файла конфигурации. Рассмотрим также переход на другой язык, Perl или Python, где синтаксис, по крайней мере, более переносимый, чем bash.
Я разработал скрипт, который использует ответы в Stack Overflow. Один из этих ответов привел к тому, что в 2004 году для DKMS-приложений были сопоставлены номера версий сотрудников Dell Employee.
Код сравнения исходного кода версии, который я упомянул в моем вопросе, не работал (по крайней мере для меня).
Примеры выходов:
───────────────────────────────────────────────────────────────────────────────
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
Этот код не так чист, как хотелось бы, и отражает то, что части написаны тремя разными людьми.