Pregunta

Si quiero verificar la cadena nula, lo haría

[ -z $mystr ]

pero ¿qué pasa si quiero comprobar si la variable se ha definido?¿O no hay distinción en las secuencias de comandos bash?

¿Fue útil?

Solución

Creo que la respuesta que busca está implícita (si no se indica) en vinko's respuesta, aunque no se explica de forma sencilla.Para distinguir si VAR está configurado pero vacío o no configurado, puede usar:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Probablemente puedas combinar las dos pruebas de la segunda línea en una con:

if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Sin embargo, si lee la documentación de Autoconf, encontrará que no recomiendan combinar términos con '-a' y recomiendo usar pruebas simples separadas combinadas con &&.No he encontrado ningún sistema en el que haya un problema;Eso no significa que no existieran (pero probablemente sean extremadamente raros hoy en día, incluso si no lo eran tan raros en el pasado lejano).

Podrás encontrar los detalles de estos y otros relacionados. expansiones de parámetros de shell, el test o [ comando y expresiones condicionales en el manual de Bash.


Recientemente me preguntaron por correo electrónico sobre esta respuesta con la pregunta:

Usas dos pruebas y entiendo bien la segunda, pero no la primera.Más precisamente, no entiendo la necesidad de una expansión variable.

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi

¿No lograría esto lo mismo?

if [ -z "${VAR}" ]; then echo VAR is not set at all; fi

Pregunta justa: la respuesta es "No, su alternativa más sencilla no hace lo mismo".

Supongamos que escribo esto antes de tu prueba:

VAR=

Su prueba dirá "VAR no está configurado en absoluto", pero la mía dirá (por implicación porque no hace eco de nada) "VAR está configurado pero su valor puede estar vacío".Pruebe este script:

(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo JL:1 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:1 VAR is not set at all; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo JL:2 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:2 VAR is not set at all; fi
)

La salida es:

JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all

En el segundo par de pruebas, la variable se establece, pero se establece en el valor vacío.Ésta es la distinción que ${VAR=value} y ${VAR:=value} hacen anotaciones.Lo mismo ocurre con ${VAR-value} y ${VAR:-value}, y ${VAR+value} y ${VAR:+value}, etcétera.


Como gili señala en su respuesta, si tu corres bash con el set -o nounset opción, entonces la respuesta básica anterior falla con unbound variable.Tiene fácil remedio:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

O podrías cancelar el set -o nounset opción con set +u (set -u siendo equivalente a set -o nounset).

Otros consejos

~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~> 

-z también funciona para variables indefinidas. Para distinguir entre un indefinido y uno definido, usaría las cosas enumeradas aquí o, con explicaciones más claras , aquí .

La forma más limpia es usar la expansión como en estos ejemplos. Para obtener todas sus opciones, consulte la sección Expansión de parámetros del manual.

Palabra alternativa:

~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED

Valor predeterminado:

~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED

Por supuesto, usaría uno de estos de manera diferente, colocando el valor que desea en lugar del 'valor predeterminado' y usando la expansión directamente, si corresponde.

Guía avanzada de secuencias de comandos bash, 10.2. Sustitución de parámetros:

  • $ {var + blahblah}: si se define var, 'blahblah' se sustituye por el expresión, de lo contrario, nulo se sustituye
  • $ {var-blahblah}: si se define var, se sustituye a sí mismo, de lo contrario 'blahblah' se sustituye
  • $ {var? blahblah}: si se define var, se sustituye, de lo contrario el La función existe con 'blahblah' como mensaje de error.


para basar la lógica de su programa en si la variable $ mystr está definida o no, puede hacer lo siguiente:

isdefined=0
${mystr+ export isdefined=1}

ahora, si isdefined = 0, entonces la variable no estaba definida, si isdefined = 1 la variable estaba definida

Esta forma de verificar las variables es mejor que la respuesta anterior porque es más elegante, fácil de leer y si su shell bash se configuró para error en el uso de variables indefinidas (set -u), el script terminará prematuramente.


Otras cosas útiles:

para tener un valor predeterminado de 7 asignado a $ mystr si no estaba definido, y dejarlo intacto de lo contrario:

mystr=${mystr- 7}

para imprimir un mensaje de error y salir de la función si la variable no está definida:

: ${mystr? not defined}

Tenga cuidado aquí porque usé ':' para no tener el contenido de $ mystr ejecutado como un comando en caso de que esté definido.

Un resumen de las pruebas.

[ -n "$var" ] && echo "var is set and not empty"
[ -z "$var" ] && echo "var is unset or empty"
[ "${var+x}" = "x" ] && echo "var is set"  # may or may not be empty
[ -n "${var+x}" ] && echo "var is set"  # may or may not be empty
[ -z "${var+x}" ] && echo "var is unset"
[ -z "${var-x}" ] && echo "var is set and empty"

https://stackoverflow.com/a/9824943/14731 contiene una mejor respuesta (una que es más legible y funciona con set -o nounset habilitado). Funciona más o menos así:

if [ -n "${VAR-}" ]; then
    echo "VAR is set and is not empty"
elif [ "${VAR+DEFINED_BUT_EMPTY}" = "DEFINED_BUT_EMPTY" ]; then
    echo "VAR is set, but empty"
else
    echo "VAR is not set"
fi

La forma explícita de verificar la variable que se está definiendo sería:

[ -v mystr ]

otra opción: la lista " índices de matriz " expansión :

$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1

la única vez que esto se expande a la cadena vacía es cuando foo está desarmado, por lo que puede verificarlo con la cadena condicional:

$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0

debería estar disponible en cualquier versión de bash > = 3.0

para no perder esta bicicleta aún más, pero quería agregar

shopt -s -o nounset

es algo que podría agregar a la parte superior de una secuencia de comandos, lo que generará un error si las variables no se declaran en ninguna parte de la secuencia de comandos. El mensaje que vería es unbound variable, pero como otros mencionan, no detectará una cadena vacía o un valor nulo. Para asegurarnos de que cualquier valor individual no esté vacío, podemos probar una variable a medida que se expande con ${mystr:?}, también conocida como expansión de signo de dólar, que produciría un error con parameter null or not set.

El Bash Reference Manual es una fuente autorizada de información sobre bash.

Aquí hay un ejemplo de prueba de una variable para ver si existe:

if [ -z "$PS1" ]; then
        echo This shell is not interactive
else
        echo This shell is interactive
fi

(De sección 6.3.2 .)

Tenga en cuenta que el espacio en blanco después del [ abierto y antes del ] es no opcional .


Consejos para usuarios de Vim

Tenía un script que tenía varias declaraciones de la siguiente manera:

export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"

Pero quería que difirieran los valores existentes. Así que los reescribí para que se vean así:

if [ -z "$VARIABLE_NAME" ]; then
        export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi

Pude automatizar esto en vim usando una expresión regular rápida:

s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r  export \1=\2\rfi\r/gc

Esto se puede aplicar seleccionando visualmente las líneas relevantes y luego escribiendo :. La barra de comandos se rellena previamente con :'<,'>. Pegue el comando anterior y presione enter.


Probado en esta versión de Vim:

VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by root@apple.com

Los usuarios de Windows pueden querer diferentes finales de línea.

Esto es lo que creo que es una forma mucho más clara de verificar si una variable está definida:

var_defined() {
    local var_name=$1
    set | grep "^${var_name}=" 1>/dev/null
    return $?
}

Úselo de la siguiente manera:

if var_defined foo; then
    echo "foo is defined"
else
    echo "foo is not defined"
fi

conjunto de llamadas sin ningún argumento ... genera todos los vars definidos ..
los últimos en la lista serían los definidos en su script ..
para que pueda canalizar su salida a algo que pueda descubrir qué cosas están definidas y qué no

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top