¿Cómo verifico si existe una variable en una declaración 'if'?

69

Necesito verificar la existencia de una variable en una ifdeclaración. Algo en el sentido de:

if [ -v $somevar ]
then
    echo "Variable somevar exists!"
else
    echo "Variable somevar does not exist!"

Y la pregunta más cercana a esto fue esta , que en realidad no responde a mi pregunta.

Interesante...
fuente
Si desea establecer $somevaren un valor / cadena si la variable no existe: ${somevar:=42}.
Cyrus
Personalmente, tiendo a verificar solo el vacío ( [ -n "$var" ]o [ ! -z "$var" ]). Creo que las comprobaciones de existencia / inexistencia son demasiado sutiles, y prefiero que mi código sea grueso y simple.
PSkocik
¿Por qué necesitas ese vs [ -n "$var" ]? Relacionado: stackoverflow.com/questions/3601515/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Respuestas:

97

En bash moderno (versión 4.2 y superior):

[[ -v name_of_var ]]

De help test:

-v VAR, verdadero si se establece la variable de shell VAR

Chris Down
fuente
3
También funciona con soportes individuales: [ -v name_of_var ].
meuh
77
tenga en cuenta que para los hashes y matrices, devuelve falso a menos que la variable tenga un elemento de clave / índice "0". Para namerefs, prueba si el objetivo está definido. No funciona para los parámetros especiales como $1, $-, $#...
Stéphane Chazelas
44
Esta característica solo se encuentra en bash builtin testo [; No está disponible en /usr/bin/test. Comparar man testcon help test.
Mark Lakata
@MarkLakata Right, porque los comandos externos no pueden conocer el estado interno del shell.
Chris Down
umm, ¿no debería ser siempre [[-v "$ name_of_var"]]?
Alexander Mills
24

Depende de lo que quieres decir con existe .

No una variable que ha sido declarado pero no asignado existe ?

¿Tiene una matriz (o hash) variable que se ha asignado una lista vacía existen ?

No una variable nameref que apunta a una variable que actualmente no se asigna existe ?

¿Se considera $-, $#, $1las variables? (POSIX no).

En conchas tipo Bourne, la forma canónica es:

if [ -n "${var+set}" ]; then
  echo '$var was set'
fi

Eso funciona para variables escalares y otros parámetros para saber si una variable se le ha asignado un valor (vacía o no, de forma automática, desde el medio ambiente, Traspaso, read, foru otros).

Para shells que tienen un comando typeseto declare, eso no informaría como conjunto las variables que se han declarado pero no se han asignado, excepto en zsh.

Para shells que admiten matrices, excepto yashy zshque no se reportarían como variables de matriz establecidas a menos que se haya establecido el elemento del índice 0.

Para bash(pero no ksh93ni zsh), para las variables de tipo de matriz asociativa , eso no se informaría como conjunto a menos que se haya establecido su elemento de clave "0".

Para ksh93y bash, para variables de tipo nameref , eso solo devuelve verdadero si la variable referenciada por nameref se considera establecida .

Para ksh, zshy bash, un enfoque potencialmente mejor podría ser:

if ((${#var[@]})); then
  echo '$var (or the variable it references for namerefs) or any of its elements for array/hashes has been set'
fi

Para ksh93, zshy bash4.4 o superior, también hay:

if typeset -p var 2> /dev/null | grep -q '^'; then
  echo '$var exists'
fi

Que informará las variables que se han establecido o declarado.

Stéphane Chazelas
fuente
1
declare -p/ typeset -ptrabaja bashahora también.
cas
1
@No CAS. No para las variables declaradas pero no establecidas. Intenta bash -c 'typeset -i a; typeset -p a'y compara con ksh93o zsh.
Stéphane Chazelas
+1 Gracias por señalarme aquí. También vea mi pregunta unix.stackexchange.com/q/280893/674
Tim
@cas, eso cambió con bash-4.4 (lanzado en septiembre de 2016), lo
edité
9

Como se menciona en la respuesta sobre SO , aquí hay una forma de verificar:

if [ -z ${somevar+x} ]; then echo "somevar is unset"; else echo "somevar is set to '$somevar'"; fi

donde $ {somevar + x} es una expansión de parámetro que se evalúa como nulo si var no está establecida y sustituye la cadena "x" de lo contrario.

El uso -n, como lo sugiere la otra respuesta, solo verificará si la variable contiene una cadena vacía. No verificará su existencia.

shivams
fuente
2
Necesita cotizar $somevarpara manejar IFS=x. O eso o citar x.
mikeserv
1
@mikeserv Gracias, me gusta aprender sobre casos extremos :) ¿Quieres decir if [ -z "${somevar+x}" ]? ¿Todavía se requeriría la cita dentro [[y ]]?
Tom Hale
@TomHale: sí, en casos excepcionales. las [ testrutinas aceptan los parámetros de la línea de comandos, por lo que las expansiones e interpretaciones habituales, tal como se ordenan de la manera habitual, se deben confiar para que, en la invocación de la prueba, se aplique lo que usted debe hacer que sea leído por cualquier línea de comandos del programa. prueba {! + "!"}
mikeserv
@mikeserv Supongo que su sí es mi segunda pregunta ... ¿Es el primero un sí también?
Tom Hale
1
+1; Esta parece ser la forma más sencilla de hacerlo cuando set -uestá vigente y la versión Bash es anterior a la 4.2.
Kyle Strand
4

POSIXY:

! (: "${somevar?}") 2>/dev/null && echo somevar unset

o puede dejar que su shell le muestre el mensaje:

(: "${somevar?}")
zsh: somevar: parameter not set
Cuonglm
fuente
@mikeserv: Sí, por supuesto, hay muchas formas de hacerlo. En primer lugar, creo que lo duplicaré con esto . Pero en esta pregunta, el OP solo desea verificar, no afirmó que desea salir o informar si la variable no está configurada, así que llegué con un cheque en el subshell.
Cuonglm
2
Bueno, lo sé, pero es precisamente porque tienes que hacerlo en un subshell como el que indica que podría no ser la mejor manera de probar aquí: esa es una acción de detención en caso de falla, no permite ningún medio simple de manejar un fracaso. Portablemente, incluso un trapsolo puede funcionar en EXIT. Eso es todo lo que digo: simplemente no se aplica como un pase / falla muy bien. Y tampoco soy yo quien habla, he hecho exactamente esto antes y me tomó un pequeño chat de comentarios como este para convencerme. Entonces, pensé que lo pagaría.
mikeserv
1
@mikeserv: Bueno, bueno, es un buen punto. Solo me pregunto si agregar eso puede hacer que el OP se confunda con la sintaxis :)
cuonglm
2
if set|grep '^somevar=' >/dev/null;then
    echo "somevar exists"
else
    echo "does not exist"
fi
Mel
fuente
Me imagino que esto es algo ineficiente, pero es muy simple y shcompatible, que es justo lo que necesito.
hoijui
2
printf ${var+'$var exists!\n'}

... no imprimirá nada cuando no lo haga. O...

printf $"var does%${var+.}s exist%c\n" \ not !

... te lo diré de cualquier manera.

puede usar el valor de retorno de una prueba para expandirse dinámicamente a la cadena de formato apropiada para su condición:

[ "${var+1}" ]
printf $"var does%.$?0s exist%c\n" \ not !

También puede printffallar en función de una sustitución ...

printf $"var does%${var+.}s exist%c\n%.${var+b}d" \
        \ not ! \\c >&"$((2${var+-1}))" 2>/dev/null

... que imprime $var does not exist!en stderr y devuelve un valor distinto de 0 cuando no $varestá configurado, pero imprime $var does exist!en stdout y devuelve 0 cuando $varestá configurado.

mikeserv
fuente
1

Esta línea simple funciona (y funciona en la mayoría de los shells POSIX):

${var+"false"} && echo "var is unset"

O, escrito en una forma más larga:

unset var

if ${var+"false"}
then
   echo "var is unset"
fi

La expansión es:

  • Si la var tiene un valor (incluso nulo), falso se reemplaza
  • Si la variable tiene "sin valor", entonces se reemplaza "sin valor" (nulo).

La ${var+"false"}expansión se expande a "nulo" o "falso".
Luego, se ejecuta "nada" o "falso", y se establece el código de salida.

No es necesario llamar al comando test( [o [[) ya que el valor de salida lo establece la (ejecución de) la expansión misma.


fuente
Sí, excepto en algunas versiones antiguas de emulación zsh in sh cuando $IFScontiene f, a, l, soe. Al igual que para otras respuestas, existe el caso de matrices, hash u otros tipos de variables que uno puede mencionar.
Stéphane Chazelas
@ StéphaneChazelas que escribí most POSIX shells. most significaIn the greatest number of instances , no todos. ... ... Entonces, sí, en una condición oscura when $IFS contains f, a, l, s or ey para un caparazón oscuro some old versions of zshesto falla: ¡Qué sorpresa !. Debo suponer que ese error se resolvió hace mucho tiempo. ... ... ¿Está proponiendo que debemos escribir el código para las conchas rotas hace mucho tiempo?
@ StéphaneChazelas También: El título de la pregunta es muy específico: Bash.
cebra binaria ???
mikeserv
0

La forma pura de la cáscara:

[ "${var+1}" ] || echo "The variable has not been set"

Script de prueba:

#!/bin/sh
echo "Test 1, var has not yet been created"
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 2, var=1"
var=1
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 3, var="
var=
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 4, unset var"
unset var
[ "${var+1}" ] || echo "The variable has not been set"
echo "Done"

Resultados:

Test 1, var has not yet been created
The variable has not been set
Test 2, var=1
Test 3, var=
Test 4, unset var
The variable has not been set
Done
Andreas Mikael Bank
fuente
1
Falla cuando la variable se establece en una cadena nula.
Tom Hale
0

Con bash 4.4.19, lo siguiente funcionó para mí. Aquí hay un ejemplo completo

$export MAGENTO_DB_HOST="anyvalue"

#!/bin/bash

if [ -z "$MAGENTO_DB_HOST" ]; then
    echo "Magento variable not set"
else
    echo $MAGENTO_DB_HOST
fi
Aftab Naveed
fuente
-1

No puede usar el ifcomando para verificar la existencia de variables declaradas en bash, sin embargo, la -vopción existe en bash más reciente, pero no es portátil y no puede usarlo en bashversiones anteriores . Porque cuando estás usando una variable si no existe, nacerá al mismo tiempo.

Por ejemplo, ¡imagine que no utilicé o asigné un valor a la MYTESTvariable, pero cuando utiliza el comando echo no muestra nada! ¡O si lo está utilizando if [ -z $MYTEST ], devuelve el valor cero! ¡No devolvió otro estado de salida, lo que le dice que esta variable no existe!

Ahora tiene dos soluciones (sin -vopción):

  1. Usando el declarecomando.
  2. Usando el setcomando.

Por ejemplo:

MYTEST=2
set | grep MYTEST
declare | grep MYTEST

¡Pero desafortunadamente estos comandos también le muestran funciones cargadas en la memoria! Puede usar el declare -p | grep -q MYTEST ; echo $?comando para obtener un resultado más limpio.

Sepahrad Salour
fuente
-1

Función para verificar si la variable se declara / desarma

incluido vacío $array=()


Además de la respuesta de @ Gilles

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

- que no encontré una forma de encapsularlo dentro de una función - Me gustaría agregar una versión simple, que se basa en parte en la respuesta de Richard Hansen , pero también aborda la trampa que ocurre con un vacío :array=()

# The first parameter needs to be the name of the variable to be checked.
# (See example below)

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}
  • Al probar primero si la variable está (des) establecida, se puede evitar la llamada a declarar, si no es necesario.
  • Sin embargo, si $1contiene el nombre de un vacío $array=(), la llamada a declarar se aseguraría de obtener el resultado correcto
  • Nunca se pasan muchos datos a / dev / null, ya que solo se llama a declarar si la variable no está configurada o una matriz vacía.


Con el siguiente código se pueden probar las funciones:

( # start a subshell to encapsulate functions/vars for easy copy-paste into the terminal
  # do not use this extra parenthesis () in a script!

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}

:;       echo -n 'a;       '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=;      echo -n 'a=;      '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a="sd";  echo -n 'a="sd";  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=();    echo -n 'a=();    '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=("");  echo -n 'a=("");  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
unset a; echo -n 'unset a; '; var_is_declared a && echo "# is declared" || echo "# is not declared"
echo ;
:;       echo -n 'a;       '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=;      echo -n 'a=;      '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a="foo"; echo -n 'a="foo"; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=();    echo -n 'a=();    '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=("");  echo -n 'a=("");  '; var_is_unset a && echo "# is unset" || echo "# is not unset"
unset a; echo -n 'unset a; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
)

El guión debería volver

a;       # is not declared
a=;      # is declared
a="foo"; # is declared
a=();    # is declared
a=("");  # is declared
unset a; # is not declared

a;       # is unset
a=;      # is not unset
a="foo"; # is not unset
a=();    # is not unset
a=("");  # is not unset
unset a; # is unset
Martin Rüegg
fuente
-1

función bash que funciona para los tipos escalares y de matriz :

definición

has_declare() { # check if variable is set at all
    local "$@" # inject 'name' argument in local scope
    &>/dev/null declare -p "$name" # return 0 when var is present
}

invocación

if has_declare name="vars_name" ; then
   echo "variable present: vars_name=$vars_name"
fi
Andrei Pozolotin
fuente