Bash one-liner para verificar se a versão é = [fechada]

8

Estou procurando o melhor bash one-liner para verificar se o número da versão é > = 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 bash deve ser > = 4. No meu script bash eu gostaria de colocar em um teste de uma linha da maneira mais elegante / eficiente / legível possível.

Eu antecipo muitas respostas para alcançar o objetivo, então todo esse trabalho será agradecido.

Embora esta questão seja específica para o número da versão bash, com a modificação as respostas podem se aplicar a outros aplicativos como o LibreOffice, Python, systemd, kernel, etc., que podem fornecer funcionalidade diferente com base no número da versão.

Estou adicionando um exemplo adicional porque pode haver muitos programas que você pode chamar em seu aplicativo e alguns podem precisar estar em uma versão específica.

Aqui está um exemplo da vida real:

$ yad --version
0.37.0 (GTK+ 3.18.9)

O YAD (Yet Another Dialog box) lançou a versão 0.39 em 27 de abril de 2017 e ainda não chegou aos repositórios do Ubuntu. Para usar novos recursos eu teria que baixar manualmente e compilar a partir da fonte. Se eu fizer isso, o teste será >= 0.39 .

Mais --version exemplos do teste (primeira linha exibida somente):

$ 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

Excelentes respostas foram postadas para verificação da versão BASH SOMENTE. Eu estou esperando por um script robusto que funciona para mais de bash. Talvez "--version" possa ser universalmente anexado após o nome do programa no script? O número da versão é comparado corretamente? ie. Um teste de cadeia simples "4.9.10 > 4.11.1" provavelmente retorna true no bash, mas deve ser false .

Comparando números de versão com separadores decimais

Existem várias maneiras de comparar dois números de versão que contêm separadores decimais, conforme respondido em estouro de pilha . A pergunta e resposta aceita foram postadas em 2010, mas estou inclinado para a resposta publicada em dezembro de 2016.

    
por WinEunuuchs2Unix 20.05.2017 / 02:08

4 respostas

10

Tente:

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

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

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

Você perguntou sobre o 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 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
    
por John1024 20.05.2017 / 02:13
6

Em vez de comparar números de versão, você pode testar diretamente o próprio recurso. declare -A retorna 2 (pelo menos no Bash 3.2) se ele não reconhece -A , então teste para 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 é uma matriz não associativa, portanto unset primeiro.)

Embora eu realmente não suponha que alguém vá backport 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 apenas recursos limitados ...

O caso mais geral de testar números de versão tem duas partes: 1) como encontrar o número de versão correto a ser testado 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 escolher o número da versão programaticamente pode ser difícil. Depois, há a questão 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. dpkg pode comparar números de versão do estilo Debian (que eu acho 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.]+).*//;q' )
if dpkg --compare-versions "$bashver" ge 4.0.0 ; then
    echo "'bash' in your path is version 4.x"
fi
    
por ilkkachu 20.05.2017 / 09:57
4

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

1. Use $ BASH_VERSION

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

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

Note que a sintaxe <<< para o here-doc não é portátil, se você for usá-lo com /bin/sh , que é o Dash no Ubuntu e pode ser algo diferente em um sistema diferente

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

Isso pode ser totalmente reescrito como função para ser usado 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

Este não é um one-liner, embora seja muito melhor. Qualidade sobre a quantidade.

2. Verifique o que está instalado

Para isso, você precisa filtrar a saída de apt-cache policy da mesma forma

$ 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

Note que isto não é portátil, uma vez que se não houver dpkg ou apt instalado em um sistema (por exemplo, RHEL ou FreeBSD), não fará nenhum bem a você.

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

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

Isso exigirá que você diga explicitamente ao usuário: "Ei, você realmente precisa do bash versão 4.3 ou acima, caso contrário o script não funcionará". Então, a responsabilidade cabe ao 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

Os scripts

bash não são portáteis porque sua sintaxe não é compatível com o shell Bourne. Se o script que você está escrevendo for usado em vários 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 alternar para um idioma diferente, Perl ou Python, em que a sintaxe é pelo menos mais portável que bash .

    
por Sergiy Kolodyazhnyy 20.05.2017 / 04:45
1

Uma linha não é possível, mas um script bash é possível

Desenvolvi um script que desenha respostas no Stack Overflow. Uma dessas respostas levou a um empregado da Dell a escrever comparações de números de versão em 2004 para a aplicação DKMS.

O código de comparação do número de versão inicial que mencionei na minha pergunta não funcionou (pelo menos para mim).

Exemplos de saídas:

───────────────────────────────────────────────────────────────────────────────
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:~$ 

O código

O script 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.

# 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.]\).*//''
        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.]\).*//''
        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

Este código não é tão limpo quanto eu gostaria e reflete o fato de que as partes são escritas por três pessoas diferentes.

    
por WinEunuuchs2Unix 21.05.2017 / 16:06