
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_VERSINFO
es 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 0
para 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 -A
devuelve 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 var
También falla si var
es una matriz no asociativa, por lo que unset
es 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 --version
o -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. dpkg
se 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_VERSION
la 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 policy
este tipo.
$ apt-cache policy bash | awk -F '[:.]' '/Installed:/{printf "%s\n",substr($2,2)}'
4
dpkg-query
Tambié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 dpkg
o apt
está 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 bash
no pueda usarlas. set -e
La línea siguiente #!/bin/bash
permitirá 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
bash
Los 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