1. Utilice $BASH_VERSION

1. Utilice $BASH_VERSION

Necesito probar si el número de versión de Bash es >= para 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 utilizar matrices asociativas, el número de versión de bash debe ser >=4.

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

Respuesta1

Intentar:

$ [ "${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 of 0para cualquier versión anterior de bash para la que esta variable no esté configurada.

Extraer información de versión de otros programas

Preguntaste sobre LibreOffice, Python, kernel, etc.

LibreOffice produce información de versión similar a la siguiente:

$ 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 pitón:

$ 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

Respuesta2

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 lo 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 lo que unsetes la primera).

Si bien realmente no asumo que nadie vaya a respaldar funciones en Bash, en general es más apropiado verificar las funciones, 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 indican 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 seleccionar el número de versión mediante programación puede resultar difícil. Luego está el problema de tener posiblemente varias versiones del mismo programa instaladas al mismo tiempo.

El segundo depende de cierto conocimiento del formato de los números de versión. dpkgse puede compararNúmeros de versión al estilo Debian(que creo que incluyesembrarversiones tipo 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

Respuesta3

Hay un par de formas de abordar lo que desea lograr.

1. Utilice $BASH_VERSION

Es suficiente con ver qué hay en $BASH_VERSIONla variable. Personalmente usaría subshell así:

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

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

La forma alternativa es mediante declaración de caso o declaración if. 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 como 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 una frase sencilla, aunque es mucho mejor. Calidad sobre cantidad.

2. Comprueba lo que está instalado

Para eso necesitas filtrar la salida de apt-cache policyeste tipo.

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

dpkg-queryTambién puede resultar útil filtrar mediante 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 lo hay dpkgo aptestá instalado en un sistema (por ejemplo, RHEL o FreeBSD), no le servirá de nada.

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

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

Esto requerirá que le digas explícitamente al usuario: "Oye, realmente necesitas la versión 4.3 o superior de bash; de lo contrario, el script no funcionará". Entonces la responsabilidad recae en el usuario, aunque algunos podrían argumentar que éste 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 Bourne Shell. Si el script que está escribiendo se va a utilizar en una variedad de sistemas diferentes, no solo en Ubuntu, entonces abandone toda esperanza y busque 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 lenguaje diferente, Perl o Python, donde la sintaxis es al menos más portátil que bash.

Respuesta4

No es posible una sola línea, pero es posible un script bash

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

El código

El siguiente script bash 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

información relacionada