Verifique se a versão do Bash é> = número da versão fornecida

11

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 matrizes associativas, o número da versão do bash deve ser> = 4.

No meu script bash, eu 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.

WinEunuuchs2Unix
fonte
Você já olhou para as variáveis $BASH_VERSIONe $BASH_VERSINFO?
steeldriver
@steeldriver Não. Essa seria uma resposta aceitável no caso específico do bash, se você quiser publicá-la. No entanto, para outros casos, as variáveis ​​ambientais podem não estar disponíveis.
WinEunuuchs2Unix
A propósito, relacionado: askubuntu.com/questions/39309/…
Sergiy Kolodyazhnyy 20/17
@SergiyKolodyazhnyy relacionado apenas ao bash. Embora eu esteja procurando por uma única linha ou script / função do bash, verificar apenas o número da versão do bash não é o objetivo final. Uma rotina genérica para verificar todos os programas anexando --versione testando a saída era a intenção original. Eu editei a pergunta de acordo.
WinEunuuchs2Unix
Os formatos dos números das versões variam muito entre os programas. A maneira como eles relatam os detalhes da versão também varia bastante. Simplesmente não é prático pedir uma maneira de verificar a versão de um programa aleatório. Esta questão é muito ampla.
muru

Respostas:

15

Experimentar:

$ [ "${BASH_VERSINFO:-0}" -ge 4 ] && echo "bash supports associative arrays"
bash supports associative arrays

BASH_VERSINFOé uma variável de matriz somente leitura cujos membros mantêm informações de versão para esta instância do bash. Desde que foi introduzido no bash 2.0, provavelmente é suportado por todas as versões do bash que você encontrará. Mas, para ser cauteloso, incluímos um valor padrão de 0para qualquer versão anterior do bash para a qual essa variável não esteja definida.

Extraindo informações de versão de outros programas

Você perguntou sobre o LibreOffice, Python, kernel, etc.

O LibreOffice produz informações de versão parecidas com:

$ 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 python:

$ 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
John1024
fonte
Ótimas respostas! Apenas observe que você uname -ré "4.9.0-2-amd64", que pode ser maior que o meu "4.11.1-041101-genérico" com um teste de bash regular quando, na realidade, meu número de versão é maior.
WinEunuuchs2Unix
2
As ferramentas Python do @ WinEunuuchs2Unix podem comparar com precisão as seqüências de versão. Veja cordas versão Comparem em Python
wjandrea
John - o exemplo de python pode ser abreviado com o $ python --versionqual retorna Python 2.7.12. @ wjandrea-- obrigado pelo link +1. Talvez eu pudesse criar uma tabela com todos os nomes de programas chamados e números mínimos de versão. Em seguida, passe a tabela para uma cópia modificada do pythonlink que você forneceu. Como apenas o Python compilado pode ser chamado por grubvocê, você acha que existe um binário para fazer isso ou é possível no shell.
WinEunuuchs2Unix 20/05
9

Em vez de comparar números de versão, você pode testar diretamente o próprio recurso. declare -Aretorna 2(pelo menos no Bash 3.2) se não reconhecer -A, então teste isso (ele também imprime um erro):

unset assoc
if ! declare -A assoc ; then
    echo "associative arrays not supported!"
    exit 1
fi

( declare -A vartambém falha se varfor uma matriz não associativa, portantounset , primeiro.)

Embora eu realmente não assuma que alguém vai fazer o backport de recursos no Bash, em geral é mais apropriado verificar os recursos, não as versões. Mesmo no caso de Bash, alguém pode compilar uma versão com apenas recursos limitados ...


O caso mais geral de testar números de versão possui 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 o número da versão com um sinalizador de linha de comando como --versionou-v , mas o formato de saída varia e a escolha programada do número da versão pode ser difícil. Depois, há o problema de possivelmente ter várias versões do mesmo programa instaladas ao mesmo tempo.

O segundo depende de algum conhecimento do formato dos números de versão. dpkgpode comparar números de versão no estilo Debian (que eu acho que inclui versões do tipo semver 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
ilkkachu
fonte
5

Existem algumas maneiras de abordar o que você deseja alcançar.

1. Use $ BASH_VERSION

É suficiente apenas ver o que está na $BASH_VERSIONvariável. Pessoalmente, eu usaria o subshell assim:

$ (read -d "." version trash <<< $BASH_VERSION; echo "$version" )
4

Observe que a <<<sintaxe do here-doc não é portátil, se você a usar /bin/sh, que é o Dash no Ubuntu e pode ser outra coisa em um sistema diferente

A maneira alternativa é via declaração de caso ou declaração de caso. 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 deve verificar se essa variável está definida, em primeiro lugar, com algo como [ -n $BASH_VERSION ]

Isso totalmente pode ser reescrito como função a ser usada em um script. Algo longo as linhas de:

#!/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 linha, embora isso seja muito melhor. Qualidade acima de quantidade.

2. Verifique o que está instalado

Para isso, você precisa filtrar a saída da apt-cache policymesma forma

$ apt-cache policy bash | awk -F '[:.]' '/Installed:/{printf "%s\n",substr($2,2)}'
4

dpkg-querytambém pode ser útil com alguns filtros 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 dpkgou estiver aptinstalado em um sistema (por exemplo, RHEL ou FreeBSD), não fará nenhum bem.

3. Use set -e para sair do script se houver um erro

Uma maneira de contornar isso é simplesmente ir em frente e usar matrizes associativas e sair quando bashnão puder usá-las. set -eA linha abaixo #!/bin/bashpermitirá que o script seja encerrado se o script não puder usar matriz associativa.

Isso exigirá que você informe explicitamente ao usuário: "Ei, você realmente precisa do bash versão 4.3 ou superior, caso contrário, o script não funcionará". A responsabilidade é do usuário, embora alguns possam argumentar que essa não é realmente uma boa abordagem para o desenvolvimento de software.

4. Abandone toda a esperança e escreva scripts portáteis, compatíveis com POSIX

bashscripts não são portáveis ​​porque sua sintaxe não é compatível com o shell Bourne. Se o script que você está escrevendo for usado em uma variedade de sistemas diferentes, não apenas no Ubuntu, abandone toda a esperança 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átil que bash.

Sergiy Kolodyazhnyy
fonte
A matriz associativa é a segunda de "duas matrizes", como você sugere na seção 4. Seu único objetivo é manter a chave "caminho / para / nome do arquivo" e o valor é o índice na matriz principal. A matriz associativa foi criada para acelerar as pesquisas na matriz indexada regular que atualmente são seqüenciais. Embora eu esteja de olho no Python de qualquer maneira, minha compatibilidade atual do bash está focada no Ubuntu e talvez no Bash para Windows 10. Gosto do seu script, que pode ser adaptado. Por exemplo, yad --versionretorna, 0.37.0 (GTK+ 3.18.9)mas novos recursos estão no momento 0.39.
WinEunuuchs2Unix 20/17/17
resposta excelente e útil. obrigado!
Qodeninja 14/08/19
2

One-liner não é possível, mas é possível um script bash

Eu desenvolvi um script que se baseia em respostas no Stack Overflow. Uma dessas respostas levou a um funcionário da Dell escrevendo comparações de números de versão em 2004 para o aplicativo DKMS.

O código

O script bash abaixo das necessidades a 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
WinEunuuchs2Unix
fonte