¿Cómo puedo evitar que las opciones 'shopt' no compatibles causen errores en mi .bashrc?

9

Trabajo en un entorno relativamente heterogéneo donde puedo ejecutar diferentes versiones de Bash en diferentes nodos HPC, máquinas virtuales o mi estación de trabajo personal. Debido a que puse mis scripts de inicio de sesión en un repositorio de Git, me gustaría usar el mismo (ish) en .bashrctodos los ámbitos, sin un montón de "si este host, entonces ..." - escribe desorden.

Yo al igual que el comportamiento predeterminado de Bash ≤ 4.1 que se expande cd $SOMEPATHen cd /the/actual/pathal pulsar la Tabtecla. En Bash 4.2 y superior, necesitaría shopt -s direxpandvolver a habilitar este comportamiento, y eso no estuvo disponible hasta 4.2.29 . Sin embargo, este es solo un ejemplo; Otra shoptopción , posiblemente relacionada , complete_fullquote(aunque no sé exactamente qué hace) también puede haber cambiado el comportamiento predeterminado en v4.2.

Sin embargo, direxpandlas versiones anteriores de Bash no lo reconocen, y si lo intento shopt -s direxpanden mi .bashrc, eso genera un mensaje de error que se imprime en la consola cada vez que inicio sesión en un nodo con un Bash anterior:

-bash: shopt: direxpand: invalid shell option name

Lo que me gustaría hacer es envolver un condicional shop -s direxpandpara habilitar esa opción en Bash> 4.1 de una manera robusta, sin irritar las versiones anteriores de Bash ( es decir , no solo redirigiendo la salida del error /dev/null).

El tipo respeta
fuente
¿Cómo mi respuesta no ayudó?
Luciano Andress Martini
@LucianoAndressMartini Lo hizo, y esa es la solución con la que terminé yendo por mi cuenta .bashrc. Todavía quería un registro de cómo usar $BASH_VERSINFOpara interrogar la versión mayor / menor del shell en ejecución, para mi propia edificación, razón por la cual terminé de publicar mi propia respuesta. :)
TheDudeAbides
Mira en mi respuesta, tengo algo sobre comparar la versión de los programas con el script de shell.
Luciano Andress Martini

Respuestas:

14

Compruebe si direxpandestá presente en la salida de shopty habilítelo si es:

shopt | grep -q '^direxpand\b' && shopt -s direxpand
Luciano Andress Martini
fuente
44
Mejor hacerlo grep -q '^direxpand\b'en caso de que alguna versión futura o bifurcación de bash tenga una opción que contenga esto como una subcadena y se elimine direxpand. Es poco probable en este caso específico, pero no cuesta mucho ser robusto.
Gilles 'SO- deja de ser malvado'
Gracias Luciano. Me intención de responder a mi propia pregunta, pero yo "ll a aceptar su respuesta después de mis ediciones pasan por la revisión por pares Tal vez se les puede aprobar a sí mismo.?
TheDudeAbides
44
Bash permite consultar opciones de shell específicas, por lo que se puede usar [ -z "$(shopt -po direxpand 2>&-)" ] || shopt -s direxpand. ¡No más problemas de expresiones regulares! :-)
David Foerster
@DavidFoerster Cambiaría la lógica: [ -n "blah" ] && shopt blahla forma en que lo expresas, estás diciendo "si no se admite direxpand, entonces no hagas esto".
Rico
1
@Rich: La mayoría de mis scripts de shell se incluyen set -een la parte superior, por lo que tiendo a utilizar la lógica de atajos de esta manera.
David Foerster
16

No veo qué hay de malo en redirigir errores /dev/null. Si desea que su código sea robusto set -e, use el idioma común … || true:

shopt -s direxpand 2>/dev/null || true

Si desea ejecutar algún código de reserva si la opción no existe, use el estado de retorno de shopt:

if shopt -s direxpand 2>/dev/null; then
   # the direxpand option exists
else
   # the direxpand option does not exist
fi

Pero si realmente no le gusta redirigir el error, puede usar el mecanismo de finalización para realizar la introspección. Esto supone que no tiene máquinas anticuadas con bash ≤ 2.03 que no tenían finalización programable.

shopt_exists () {
  compgen -A shopt -X \!"$1" "$1" >/dev/null
}
if shopt_exists direxpand; then
  shopt -s direxpand
fi

Este método evita la bifurcación, que es lenta en algunos entornos como Cygwin. Lo mismo ocurre con el sencillo 2>/dev/null, no creo que se pueda superar eso en el rendimiento.

Gilles 'SO- deja de ser malvado'
fuente
Ahí no es donde habría ido mi cerebro, pero me gusta la compgenpropuesta. ¡Eso es material de nivel universitario allí mismo! Evitar la redirección a /dev/nulles solo una preferencia personal. Me gusta pedir permiso en lugar de perdón, si eso tiene sentido. :)
TheDudeAbides
+1 para una escolarización totalmente inesperada en la finalización programable de Bash, lo que me obligó a ir al manual para descifrar lo que compgen -A shopt -X ...incluso significaba.
TheDudeAbides
44
@TheDudeAbides Leí sobre el uso de compgenesta manera en Unix y Linux , no sé quién lo propuso por primera vez. (Dejé de usar bash como mi shell principal antes de que tuviera una finalización programable). En la programación, generalmente es una mala idea pedir permiso porque existe el riesgo de que la verificación de permisos no coincida con lo que realmente está haciendo, ya sea debido a una codificación error (donde no está comprobando lo que cree que está comprobando) o porque lo que comprobó cambió antes de usarlo .
Gilles 'SO- deja de ser malvado'
5

Cuando sepa con certeza que una shoptopción específica está disponible en una versión principal / menor / parche de Bash, puede inspeccionar la $BASH_VERSIONvariable o los elementos de la $BASH_VERSINFO[]matriz para habilitarla condicionalmente.

Aquí hay una prueba para Bash 4.2.29 o superior, la versión donde direxpand se introdujo por primera vez a la serie 4.2:

if [[ $BASH_VERSION == 4.2.* && ${BASH_VERSINFO[2]} -ge 29 ]] ||
   [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 3 ]] ||
   [[ ${BASH_VERSINFO[0]} -ge 5 ]]; then
    shopt -s direxpand
fi

Editar: Para ser claros, esta es una solución ridículamente sobredimensionada para simplemente ignorar un mensaje de error proveniente de sus scripts de inicio de sesión, pero quería documentarlo de todos modos, para mi propia edificación.

Tenga en cuenta los corchetes , que son obligatorios, y el uso de y , que hacen comparaciones léxicas enteras en lugar de (dependientes de la localidad). Si no se cita, el RHS del operador se trata como patrones "extglob" dentro de Bash / condicionales, como se indica aquí , lo que hace una comparación más estética "comienza con" que una expresión regular, IMO.${BASH_VERSINFO[index]}-eq-gt==[[]]

La $BASH_VERSINFOmatriz contiene toda la información que vería en la salida de bash --version:

bash --version | head -1
# result:
# GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

declare -p BASH_VERSINFO
# result:
# declare -ar BASH_VERSINFO='([0]="4" [1]="3" [2]="48" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")'

Cuando no está claro en la documentación para la shoptcual las versiones de Bash se admitieron o cambiaron su comportamiento, el método propuesto por Luciano está bien:

# note the '-q' so that the matched pattern isn't actually printed
shopt | grep -q direxpand && shopt -s direxpand

... como es la solución propuesta por Gilles de simplemente ignorar el error ( shopt -s direxpand 2>/dev/null) y quizás verificar $?si es absolutamente necesario.

Referencias: 1 , 2 , 3
Lectura relacionada: Set y Shopt - ¿Por qué dos?

El tipo respeta
fuente
También es posible que pueda usar algo como if [[ $BASH_VERSION > 4.3 ]];(que coincide 4.3.0, 5.0etc., pero también 4.3.0-alpha. No sé si el hecho posterior importa.)
ilkkachu
Hola @ilkkachu Gracias por su edición para cubrir Bash v5.x. Sin direxpandembargo, la opción está disponible para Bash 4.2; He verificado esto una con la imagen del estibador en v4.2.53 ejecutando docker run --rm bash:4.2 bash -c shopt | grep direxpand(y, por añadidura, que de hecho es no disponible en v4.1.17 ejecutando docker run --rm bash:4.1 bash -c shopt | grep direxpand).
TheDudeAbides
ah ok, probé 4.2.0y me topé con el hecho de que no funcionó allí. El registro de cambios también menciona que se agregó bash-4.3-alpha. Supongo que sería necesario verificarlo ${BASH_VERSINFO[2]}para ser exacto al respecto, pero no sé qué versión lo agregó ...
ilkkachu
Creo que básicamente hemos demostrado el punto que Gilles hizo arriba; que, de hecho, es mejor intentar habilitar la opción de shell y luego tratar el error (o suprimirlo) si no es compatible.
TheDudeAbides