Pruebe si una variable está configurada en bash cuando se usa "set -o nounset"

95

El siguiente código sale con un error de variable independiente. ¿Cómo solucionar esto, sin dejar de usar la set -oopción de conjunto de nombres?

#!/bin/bash

set -o nounset

if [ ! -z ${WHATEVER} ];
 then echo "yo"
fi

echo "whatever"
vinodkone
fuente

Respuestas:

98
#!/bin/bash

set -o nounset


VALUE=${WHATEVER:-}

if [ ! -z ${VALUE} ];
 then echo "yo"
fi

echo "whatever"

En este caso, VALUEtermina siendo una cadena vacía si WHATEVERno se establece. Estamos usando la {parameter:-word}expansión, que puede buscar en man bash"Expansión de parámetros".

Angelom
fuente
14
simplemente reemplácelo si [! -z $ {VALOR}]; con if [! -z $ {LO QUE SEA: -}];
Angelom
18
:-comprueba si la variable no está definida o está vacía. Si desea comprobar únicamente si se trata de desarmar, utilice -: VALUE=${WHATEVER-}. Además, una forma más legible de comprobar si una variable está vacía:if [ "${WHATEVER+defined}" = defined ]; then echo defined; else echo undefined; fi
l0b0
1
Además, esto no funcionará si $WHATEVERsolo contiene espacios en blanco: vea mi respuesta.
l0b0
10
¿Hay alguna razón para usar " ! -z" en lugar de solo " -n"?
Jonathan Hartley
31

Debe citar las variables si desea obtener el resultado que espera:

check() {
    if [ -n "${WHATEVER-}" ]
    then
        echo 'not empty'
    elif [ "${WHATEVER+defined}" = defined ]
    then
        echo 'empty but defined'
    else
        echo 'unset'
    fi
}

Prueba:

$ unset WHATEVER
$ check
unset
$ WHATEVER=
$ check
empty but defined
$ WHATEVER='   '
$ check
not empty
l0b0
fuente
He intentado esto y me sorprende esto funciona ... todo es correcto, excepto de acuerdo con "info bash", "${WHATEVER-}"debe tener un ":"(colon) antes de la "-"(guión) como: "${WHATEVER:-}", y "${WHATEVER+defined}"debe tener un colon antes de la "+"(más) como: "${WHATEVER:+defined}". Para mí, funciona de cualquier manera, con o sin los dos puntos. En algunas versiones de 'nix probablemente no funcionará sin incluir los dos puntos, por lo que probablemente debería agregarse.
Kevin Fegan
8
No, -, +, :+, y :-son compatibles. Los primeros detectan si la variable está configurada y los segundos detectan si está configurada o vacía . De man bash: "Omitir los dos puntos da como resultado una prueba solo para un parámetro que no está configurado".
l0b0
1
No importa =). Estás en lo correcto. No sé cómo me perdí eso.
Kevin Fegan
De los documentos : Dicho de otra manera, si se incluyen los dos puntos, el operador prueba la existencia de ambos parámetros y que su valor no es nulo; si se omiten los dos puntos, el operador solo prueba la existencia.
Acumenus
8

¿Qué tal un delineador?

[ -z "${VAR:-}" ] && echo "VAR is not set or is empty" || echo "VAR is set to $VAR"

-z comprueba tanto la variable vacía como la no definida

NublaII
fuente
2
No, -zsolo comprueba si el siguiente parámetro está vacío. -zes solo un argumento del [comando. La expansión variable ocurre antes de que [ -zpueda hacer nada.
dolmen
1
Esta parece la solución correcta, ya que no genera un error si $ VAR no está configurado. @dolmen, ¿puedes dar un ejemplo de cuándo no funcionaría?
Chris Stryczynski
@dolmen después de leer varios recursos de bash sobre la expansión de parámetros y encontrar las otras respuestas demasiado complicadas, no veo nada malo en esta. por lo que su "aclaración", aunque técnicamente correcta, parece bastante inútil en la práctica, a menos que necesite diferenciar entre desarmado y vacío. Probé sin configurar, vacío y no vacío (bash 4) y prácticamente hizo lo que se anuncia cada vez.
JL Peyret
8

Supuestos:

$ echo $SHELL
/bin/bash
$ /bin/bash --version | head -1
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
$ set -o nounset

Si desea que un script no interactivo imprima un error y salga si una variable es nula o no está establecida:

$ [[ "${HOME:?}" ]]

$ [[ "${IAMUNBOUND:?}" ]]
bash: IAMUNBOUND: parameter null or not set

$ IAMNULL=""
$ [[ "${IAMNULL:?}" ]]
bash: IAMNULL: parameter null or not set

Si no desea que se cierre el script:

$ [[ "${HOME:-}" ]] || echo "Parameter null or not set."

$ [[ "${IAMUNBOUND:-}" ]] || echo "Parameter null or not set."
Parameter null or not set.

$ IAMNULL=""
$ [[ "${IAMUNNULL:-}" ]] || echo "Parameter null or not set."
Parameter null or not set.

Incluso puedes usar [y en ]lugar de[[ y]] superior, pero este último es preferible en Bash.

Tenga en cuenta lo que hacen los dos puntos arriba. De los documentos :

Dicho de otra manera, si se incluyen los dos puntos, el operador prueba la existencia de ambos parámetros y que su valor no es nulo; si se omiten los dos puntos, el operador solo prueba la existencia.

Al parecer, no hay necesidad de -no -z.

En resumen, normalmente puedo usar [[ "${VAR:?}" ]]. Según los ejemplos, esto imprime un error y sale si una variable es nula o no está establecida.

Acumenus
fuente
4

Puedes usar

if [[ ${WHATEVER:+$WHATEVER} ]]; then

pero

if [[ "${WHATEVER:+isset}" == "isset" ]]; then

podría ser más legible.

Aleš Friedl
fuente
Las comparaciones de cadenas deben usar el =operador estándar (POSIX) , no ==para ayudar en la portabilidad, y en [lugar de hacerlo [[si es posible.
Jens
2
@Jens La pregunta es específica de bash e incluye set -o nounsetcuál es específica de bash. Si coloca un #!/bin/bashen la parte superior de su secuencia de comandos, en realidad es mejor usar las mejoras de bash.
Bruno Bronosky
0

Si bien este no es exactamente el caso de uso solicitado anteriormente, descubrí que si desea usar nounset(o -u) el comportamiento predeterminado es el que desea: salir de cero con un mensaje descriptivo.

Me tomó bastante tiempo darme cuenta de esto y pensé que valía la pena publicarlo como solución.

Si todo lo que quieres es hacer eco de algo más al salir o hacer algo de limpieza, puedes usar una trampa.

El :-operador es probablemente lo que desea de otra manera.

Max Bileschi
fuente