¿Cómo probar si una variable está definida en Bash antes de la versión 4.2 con la opción de shell nounset?

16

Para las versiones de Bash anteriores a "GNU bash, Version 4.2", ¿hay alternativas equivalentes para la -vopción del testcomando? Por ejemplo:

shopt -os nounset
test -v foobar && echo foo || echo bar
# Output: bar
foobar=
test -v foobar && echo foo || echo bar
# Output: foo
Tim Friske
fuente
-vno es una opción test, sino un operador para expresiones condicionales.
StackExchange for All
@Tim Son tres cosas, además de ser un token, una cadena y parte de una línea: An optiona un comando test -v, an operatora ay conditional expressiona unary test primarypara [ ]. No mezcles el idioma inglés con las definiciones de shell.

Respuestas:

36

Portátil para todos los proyectiles POSIX:

if [ -n "${foobar+1}" ]; then
  echo "foobar is defined"
else
  echo "foobar is not defined"
fi

Haga eso ${foobar:+1}si desea tratar de foobarla misma manera si está vacío o no está definido. También puede usar ${foobar-}para obtener una cadena vacía cuando foobarno está definida y el valor de lo foobarcontrario (o poner cualquier otro valor predeterminado después de -).

En ksh, si foobarse declara pero no se define, como en typeset -a foobar, entonces${foobar+1} expande a la cadena vacía.

Zsh no tiene variables declaradas pero no establecidas: typeset -a foobarcrea una matriz vacía.

En bash, las matrices se comportan de una manera diferente y sorprendente. ${a+1}solo se expande a 1si aes una matriz no vacía, por ejemplo

typeset -a a; echo ${a+1}    # prints nothing
e=(); echo ${e+1}            # prints nothing!
f=(''); echo ${f+1}          # prints 1

El mismo principio se aplica a las matrices asociativas: las variables de matriz se tratan como se define si tienen un conjunto de índices no vacío.

Una forma diferente y específica de bash de probar si se ha definido una variable de cualquier tipo es verificar si está incluida en la lista . Esto informa que las matrices vacías están definidas, a diferencia , pero informa que las variables declaradas pero no asignadas ( ) no están definidas.${!PREFIX*}${foobar+1}unset foobar; typeset -a foobar

case " ${!foobar*} " in
  *" foobar "*) echo "foobar is defined";;
  *) echo "foobar is not defined";;
esac

Esto es equivalente a probar el valor de retorno de typeset -p foobarodeclare -p foobar , excepto quetypeset -p foobar falla en variables declaradas pero no asignadas.

En bash, como en ksh, set -o nounset; typeset -a foobar; echo $foobardesencadena un error en el intento de expandir la variable indefinida foobar. A diferencia de ksh, set -o nounset; foobar=(); echo $foobar(o echo "${foobar[@]}") también desencadena un error.

Tenga en cuenta que en todas las situaciones descritas aquí, se ${foobar+1}expande a la cadena vacía si y solo si $foobarcausaría un error debajo set -o nounset.

Gilles 'SO- deja de ser malvado'
fuente
1
¿Qué pasa con las matrices? En la versión Bash "GNU bash, versión 4.1.10 (4) -release (i686-pc-cygwin)" echo "${foobar:+1}"no se imprime 1si declare -a foobarse emitió anteriormente y, por lo tanto, foobares una matriz indexada. declare -p foobarinforma correctamente declare -a foobar='()'. ¿ "${foobar:+1}"Solo funciona para variables sin matriz?
Tim Friske
@TimFriske ${foobar+1}(sin el :, invertí dos ejemplos en mi respuesta original) es correcto para las matrices en bash si su definición de "definido" es " $foobarfuncionaría en set -o nounset". Si tu definición es diferente, bash es un poco raro. Ver mi respuesta actualizada.
Gilles 'SO- deja de ser malvado'
1
Con respecto al tema "En bash, las matrices se comportan de una manera diferente y sorprendente". el comportamiento puede explicarse en las páginas de manual de bash (1), sección "Arreglos". Establece que "Hacer referencia a una variable de matriz sin un subíndice es equivalente a hacer referencia a la matriz con un subíndice de 0". Por lo tanto, si ni un 0índice ni una clave se definen como verdaderos a=(), ${a+1}correctamente no devuelve nada.
Tim Friske
1
@TimFriske Sé que la implementación de bash se ajusta a su documentación. Pero tratar una matriz vacía como una variable indefinida es un diseño realmente extraño.
Gilles 'SO- deja de ser malvado'
Prefiero el resultado de: ¿Cómo verificar si una variable está configurada en Bash? -> De la manera correcta ... Toco un acorde con cómo creo que esto debería funcionar. Me atrevo a decir que Windows tiene un definedoperador. Test podría hacer eso; no puede ser difícil ( um ...)
será el
5

Para resumir con la respuesta de Gilles, inventé mis siguientes reglas:

  1. Usar [[ -v foobar ]]para variables en la versión Bash> = 4.2.
  2. Uso declare -p foobar &>/dev/nullpara variables de matriz en la versión Bash <4.2.
  3. Use (( ${foo[0]+1} ))o (( ${bar[foo]+1} ))para subíndices de arrays indexados ( -a) y codificados -A( declare), respectivamente. Las opciones 1 y 2 no funcionan aquí.
Tim Friske
fuente
3

Utilizo la misma técnica para todas las variables en bash, y funciona, por ejemplo:

[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

salidas:

foobar is unset

mientras que

foobar=( "val" "val2" )
[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"

salidas:

foobar is set
Stein Inge Morisbak
fuente
Tuve que eliminar [@] si la matriz tiene más de un valor.
Stein Inge Morisbak
1
Esto funciona muy bien, siempre que desee probar si tiene un valor, no si se ha definido. Es decir, foobar=""luego informaré eso foobar is unset. No, espera, lo retiro. Realmente solo prueba si el primer elemento está vacío o no, por lo que parece ser una buena idea si sabe que la variable NO es una matriz, y solo le importa el vacío, no la definición.
Ron Burk
solo funciona si sus scripts se ejecutan con variables indefinidas permitidas (sin set -u)
Florian Heigl