
Preciso testar se o número da versão do Bash é >= para um número específico. Por exemplo eu tenho:
$ 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.
Para usar arrays associativos, o número da versão do bash deve ser >=4.
No meu script bash, gostaria de fazer um teste de uma linha da maneira mais elegante/eficiente/legível possível, mas outras abordagens também são aceitas.
Responder1
Tentar:
$ [ "${BASH_VERSINFO:-0}" -ge 4 ] && echo "bash supports associative arrays"
bash supports associative arrays
BASH_VERSINFO
é uma variável de array somente leitura cujos membros contêm informações de versão para esta instância do bash. Como foi introduzido no bash 2.0, provavelmente é compatível com todas as versões do bash que você encontrar. Mas, para sermos cautelosos, incluímos um valor padrão of 0
para qualquer versão anterior do bash para a qual esta variável não esteja definida.
Extraindo informações de versão de outros programas
Você perguntou sobre LibreOffice, Python, kernel, etc.
O LibreOffice produz informações de versão semelhantes a:
$ libreoffice --version
LibreOffice 5.2.6.2 20m0(Build:2)
Para extrair o número da versão:
$ libreoffice --version | cut -d' ' -f2
5.2.6.2
Para píton:
$ python -c 'import platform; print(platform.python_version())'
2.7.13
Para obter a versão do kernel, use uname
:
$ uname -r
4.9.0-2-amd64
Responder2
Em vez de comparar os números de versão, você pode testar diretamente o próprio recurso. declare -A
retorna 2
(pelo menos no Bash 3.2) se não reconhecer -A
, então teste isso (também imprime um erro):
unset assoc
if ! declare -A assoc ; then
echo "associative arrays not supported!"
exit 1
fi
( declare -A var
também falha se var
for uma matriz não associativa, então unset
primeiro.)
Embora eu realmente não presuma que alguém fará backport de recursos no Bash, em geral é mais apropriado verificar os recursos, não as versões. Mesmo no caso do Bash, alguém pode compilar uma versão com recursos limitados...
O caso mais geral de teste de números de versão tem duas partes: 1) como encontrar o número de versão correto para testar e 2) como compará-lo com outro valor.
O primeiro é o mais difícil. Muitos programas informam seu número de versão com um sinalizador de linha de comando como --version
ou -v
, mas o formato de saída varia e pode ser difícil escolher programaticamente o número da versão. Depois, há a questão de possivelmente ter várias versões do mesmo programa instaladas ao mesmo tempo.
A segunda depende de algum conhecimento do formato dos números de versão. dpkg
pode compararNúmeros de versão no estilo Debian(que eu acho que incluisempredigite versões como subconjunto):
if dpkg --compare-versions 4.3.30 ge 4.0.0 ; then
echo "it's version 4.x"
fi
Ou, apenas para combinar o acima:
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
Responder3
Existem algumas maneiras de abordar o que você deseja alcançar.
1. Utilize$BASH_VERSION
É suficiente apenas ver o que está na $BASH_VERSION
variável. Pessoalmente, eu usaria subshell assim:
$ (read -d "." version trash <<< $BASH_VERSION; echo "$version" )
4
Observe que <<<
a sintaxe para here-doc não é portátil, se você for usá-la com /bin/sh
, que é Dash no Ubuntu e pode ser outra coisa em um sistema diferente
A maneira alternativa é por meio da instrução case ou da instrução if. Pessoalmente, eu faria isso:
bash-4.3$ case $BASH_VERSION in 4.*) echo "Can use associative arrays";; ?) echo "can't use associative arrays" ;; esac
Can use associative arrays
Provavelmente por uma questão de portabilidade, você provavelmente deveria verificar se essa variável está definida em primeiro lugar com algo como[ -n $BASH_VERSION ]
Isso pode ser totalmente reescrito como uma função a ser usada em um script. Algo muito longo como:
#!/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
Esta não é uma frase única, embora seja muito melhor. Qualidade acima de quantidade.
2. Verifique o que está instalado
Para isso você precisa filtrar a saída apt-cache policy
assim
$ apt-cache policy bash | awk -F '[:.]' '/Installed:/{printf "%s\n",substr($2,2)}'
4
dpkg-query
também pode ser útil com alguma filtragem via awk
.
$ dpkg-query -W bash | awk '{print substr($2,1,1)}'
4
Observe que isso não é portátil, pois se não houver dpkg
ou apt
estiver instalado em um sistema (por exemplo, RHEL ou FreeBSD), não adiantará nada.
3. Use set -e para sair do script se houver um erro
Uma maneira de contornar isso é simplesmente usar matrizes associativas e sair quando bash
não puder usá-las. set -e
A linha abaixo #!/bin/bash
permitirá que o script seja encerrado se o script não puder usar a matriz associativa.
Isso exigirá que você diga explicitamente ao usuário: "Ei, você realmente precisa do bash versão 4.3 ou superior, caso contrário o script não funcionará". Então a responsabilidade recai sobre o usuário, embora alguns possam argumentar que esta não é realmente uma boa abordagem para o desenvolvimento de software.
4. Abandone toda esperança e escreva scripts portáteis compatíveis com POSIX
bash
scripts não são portáveis porque sua sintaxe não é compatível com o Bourne Shell. Se o script que você está escrevendo for usado em vários sistemas diferentes, não apenas no Ubuntu, abandone todas as esperanças e encontre maneiras de usar algo diferente de matrizes associativas. Isso pode incluir ter duas matrizes ou analisar um arquivo de configuração. Considere também mudar para uma linguagem diferente, Perl ou Python, onde a sintaxe é pelo menos mais portável que bash
.
Responder4
One-liner não é possível, mas um script bash é possível
Desenvolvi um script baseado em respostas no Stack Overflow. Uma dessas respostas levou um funcionário da Dell a escrever comparações de números de versão em 2004 para o aplicativo DKMS.
O código
O script bash abaixo precisa ser marcado como executável usando o comando chmod a+x script-name
. Estou usando o nome /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