Verifique si la versión Bash es> = número de versión dado

11

Necesito probar si el número de versión de Bash es> = a un número específico. Por ejemplo tengo:

$ 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 matrices asociativas, el número de versión de bash debe ser> = 4.

En mi script bash, me gustaría poner una prueba de una sola línea de la manera más elegante / eficiente / legible posible, pero también se aceptan otros enfoques.

WinEunuuchs2Unix
fuente
¿Has mirado las variables $BASH_VERSIONy $BASH_VERSINFO?
steeldriver
@steeldriver No. Esa sería una respuesta aceptable en un caso específico de bash si desea publicarlo. Sin embargo, para otros casos, las variables ambientales pueden no estar disponibles.
WinEunuuchs2Unix
Por cierto, relacionado: askubuntu.com/questions/39309/…
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy que está relacionado solo con bash. Aunque estoy buscando un bash one-liner o script / function, verificar el número de versión de bash solo no es el objetivo final. La --versionintención original era una rutina genérica para verificar todos los programas agregando y probando el resultado. He editado la pregunta en consecuencia.
WinEunuuchs2Unix
Los formatos de número de versión varían enormemente entre los programas. La forma en que informan los detalles de la versión también varía enormemente. Simplemente no es práctico pedir una forma de verificar la versión de un programa aleatorio. Esta pregunta es demasiado amplia.
muru

Respuestas:

15

Tratar:

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

BASH_VERSINFOes una variable de matriz de solo lectura cuyos miembros contienen información de versión para esta instancia de bash. Dado que se introdujo con bash 2.0, es probable que sea compatible con todas las versiones de bash que encontrará. Pero, para ser cautelosos, incluimos un valor predeterminado de 0para cualquier versión anterior de bash para la que esta variable no esté establecida.

Extraer información de versión de otros programas

Preguntaste sobre LibreOffice, Python, kernel, etc.

LibreOffice produce información de versión que se parece a:

$ libreoffice --version
LibreOffice 5.2.6.2 20m0(Build:2)

Para extraer el número de versión:

$ libreoffice --version | cut -d' ' -f2
5.2.6.2

Para python:

$ python -c 'import platform; print(platform.python_version())'
2.7.13

Para obtener la versión del kernel, use uname:

$ uname -r
4.9.0-2-amd64
John1024
fuente
Grandes respuestas! Solo una nota de que uname -res "4.9.0-2-amd64", que podría probar más que el mío "4.11.1-041101-generic" con una prueba de bash regular cuando en realidad mi número de versión es mayor.
WinEunuuchs2Unix
2
@ WinEunuuchs2Unix Las herramientas de Python pueden comparar con precisión las cadenas de versión. Consulte Comparar cadenas de versión en Python
wjandrea
John: el ejemplo de Python se puede acortar con lo $ python --versionque regresa Python 2.7.12. @ wjandrea-- gracias por el enlace +1. Tal vez podría construir una tabla de todos los nombres de programas llamados y números mínimos de versión. Luego pase la tabla a una copia modificada del pythonenlace que proporcionó. Debido a que solo Python compilado puede ser llamado por grubusted, pensaría que existe un binario para hacer esto o es posible en shell.
WinEunuuchs2Unix
9

En lugar de comparar números de versión, puede probar directamente la función en sí. declare -Adevuelve 2(al menos en Bash 3.2) si no reconoce -A, así que pruébelo (también imprime un error):

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

( declare -A vartambién falla si vares una matriz no asociativa, por unsetlo que primero).

Si bien realmente no asumo que alguien va a hacer backport de características en Bash, en general es más apropiado verificar las características, no las versiones. Incluso en el caso de Bash, alguien podría compilar una versión con características limitadas ...


El caso más general de probar números de versión tiene dos partes: 1) cómo encontrar el número de versión correcto para probar, y 2) cómo compararlo con otro valor.

El primero es el más difícil. Muchos programas dicen su número de versión con un indicador de línea de comando como --versiono -v, pero el formato de salida varía y elegir el número de versión mediante programación puede ser difícil. Luego está el problema de posiblemente tener varias versiones del mismo programa instaladas al mismo tiempo.

El segundo depende de cierto conocimiento del formato de los números de versión. dpkgpuede comparar los números de versión de estilo Debian (que creo que incluye versiones de tipo semver como subconjunto):

if dpkg --compare-versions 4.3.30 ge 4.0.0 ; then
    echo "it's version 4.x"
fi

O, simplemente para combinar lo anterior:

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
fuente
5

Hay dos formas de acercarse a lo que desea lograr.

1. Use $ BASH_VERSION

Es suficiente solo ver lo que hay en la $BASH_VERSIONvariable. Personalmente usaría subshell así:

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

Tenga en cuenta que la <<<sintaxis para here-doc no es portátil, si la va a usar /bin/sh, que es Dash en Ubuntu y podría ser otra cosa en un sistema diferente

La forma alternativa es a través de la declaración del caso o si la declaración. Personalmente, haría esto:

bash-4.3$ case $BASH_VERSION in 4.*) echo "Can use associative arrays";; ?) echo "can't use associative arrays" ;; esac
Can use associative arrays

Probablemente, en aras de la portabilidad, probablemente debería comprobar si dicha variable está configurada en primer lugar con algo como [ -n $BASH_VERSION ]

Esto se puede reescribir totalmente como una función para usar en un script. Algo largo las líneas 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

Esto no es un trazador de líneas, aunque esto es mucho mejor. Calidad sobre cantidad.

2. Compruebe lo que está instalado

Para eso necesitas filtrar la salida de esta apt-cache policymanera

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

dpkg-queryTambién puede ser útil con algunos filtros a través de awk.

$ dpkg-query -W bash | awk '{print substr($2,1,1)}'   
4

Tenga en cuenta que esto no es portátil, ya que si no hay dpkgo está aptinstalado en un sistema (por ejemplo, RHEL o FreeBSD), no le servirá de nada.

3. Use set -e para salir del script si hay un error

Una forma de evitarlo es simplemente usar matrices asociativas y salir cuando bashno pueda usarlas. set -ela línea a continuación #!/bin/bashpermitirá que el script se cierre si el script no puede usar una matriz asociativa.

Esto requerirá que le diga explícitamente al usuario: "Oye, realmente necesitas bash versión 4.3 o superior, de lo contrario el script no funcionará". Luego, la responsabilidad recae en el usuario, aunque algunos podrían argumentar que este no es realmente un buen enfoque para el desarrollo de software.

4. Abandone toda esperanza y escriba scripts portátiles compatibles con POSIX

bashlos scripts no son portátiles porque su sintaxis no es compatible con el shell Bourne. Si el script que está escribiendo se va a usar en una variedad de sistemas diferentes, no solo en Ubuntu, abandone toda esperanza y encuentre formas de usar algo más que matrices asociativas. Eso podría incluir tener dos matrices o analizar un archivo de configuración. Considere también cambiar a un idioma diferente, Perl o Python, donde la sintaxis es al menos más portátil que bash.

Sergiy Kolodyazhnyy
fuente
La matriz asociativa es la segunda de "dos matrices" como sugiere en la sección 4. Su único propósito es mantener la clave "ruta / a / nombre-archivo" y el valor es el índice dentro de la matriz principal. La matriz asociativa se creó para acelerar las búsquedas en la matriz indexada regular que actualmente son secuenciales. Aunque de todos modos estoy pendiente de Python, mi compatibilidad actual de bash se centra en Ubuntu y quizás en Bash para Windows 10. Me gusta su script que se puede adaptar. Por ejemplo, yad --versionvuelve 0.37.0 (GTK+ 3.18.9)pero actualmente hay nuevas características 0.39.
WinEunuuchs2Unix
Gran y útil respuesta. ¡gracias!
qodeninja
2

One-liner no es posible pero es posible un script bash

Desarrollé un script que se basa en respuestas en Stack Overflow. Una de esas respuestas llevó a un empleado de Dell a escribir comparaciones de números de versión en 2004 para la aplicación DKMS.

El código

El script bash a continuación debe marcarse como ejecutable mediante el comando chmod a+x script-name. Estoy usando el nombre /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
fuente