¿Cómo verificar si una variable está configurada en Bash?


1568

¿Cómo sé si una variable está configurada en Bash?

Por ejemplo, ¿cómo verifico si el usuario le dio el primer parámetro a una función?

function a {
    # if $1 is set ?
}

12
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.
Jens

211
Nota para los buscadores de soluciones: Hay muchas respuestas altamente calificadas a esta pregunta que responden la pregunta "es variable no vacía". Las soluciones de corrección más ("es un conjunto variable") se mencionan en las respuestas de Jens y Lionel a continuación.
Nathan Kidd

8
También Russell Harmon y Seamus están en lo correcto con su -vprueba, aunque aparentemente esto solo está disponible en nuevas versiones bashy no es portátil en todos los shells.
Graeme

55
Como señaló @NathanKidd, Lionel y Jens dan las soluciones correctas. prosseek, debe cambiar su respuesta aceptada a una de estas.
Garrett

3
... o la respuesta incorrecta podría ser rechazada por los más exigentes entre nosotros, ya que @prosseek no está abordando el problema.
dan3

Respuestas:


2305

(Por lo general) la forma correcta

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

donde ${var+x}es una expansión de parámetros que se evalúa como nada si no varestá establecida, y sustituye la cadena de lo xcontrario

Digresión de cotizaciones

Las citas se pueden omitir (por lo que podemos decir en ${var+x}lugar de "${var+x}") porque esta sintaxis y uso garantiza que esto solo se expandirá a algo que no requiere comillas (ya que se expande a x(que no contiene saltos de palabras, por lo que no necesita comillas) o nada (lo que da como resultado [ -z ], que convenientemente se evalúa al mismo valor (verdadero) que también lo [ -z "" ]hace)).

Sin embargo, si bien las citas se pueden omitir de manera segura, y no fue inmediatamente obvio para todos (ni siquiera fue evidente para el primer autor de esta explicación de citas que también es un codificador principal de Bash), a veces sería mejor escribir la solución con comillas como [ -z "${var+x}" ], al muy bajo costo posible de una penalización de velocidad O (1). El primer autor también agregó esto como un comentario al lado del código usando esta solución que proporciona la URL a esta respuesta, que ahora también incluye la explicación de por qué las citas pueden omitirse de manera segura.

(A menudo) El camino equivocado

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

Esto a menudo es incorrecto porque no distingue entre una variable que no está establecida y una variable que se establece en la cadena vacía. Es decir, si var='', entonces la solución anterior generará "var está en blanco".

La distinción entre unset y "set to the empty string" es esencial en situaciones en las que el usuario tiene que especificar una extensión, o una lista adicional de propiedades, y que al no especificarlas se predetermina a un valor no vacío, mientras que especificar la cadena vacía debería hacer que el script use una extensión vacía o una lista de propiedades adicionales.

Sin embargo, la distinción puede no ser esencial en todos los escenarios. En esos casos [ -z "$var" ]estará bien.


29
@Garrett, su edición ha hecho que esta respuesta sea incorrecta, ${var+x}es la sustitución correcta para usar. El uso [ -z ${var:+x} ]no produce un resultado diferente que [ -z "$var" ].
Graeme

11
Esto no funciona Obtengo "no establecido" independientemente de si var está establecido en un valor o no (borrado con "unset var", "echo $ var" no produce salida).
Brent212

10
Para la sintaxis de la solución $ {parámetro + palabra}, la sección del manual oficial es gnu.org/software/bash/manual/… ; sin embargo, un error en eso, no menciona muy claramente esta sintaxis, pero dice solo una cita (Omitir los dos puntos [":"] da como resultado una prueba solo para un parámetro que no está establecido ... $ {parámetro: + palabra} [significa] Si el parámetro es nulo o no establecido, no se sustituye nada; de lo contrario, se sustituye la expansión de la palabra); la referencia citada pubs.opengroup.org/onlinepubs/9699919799/utilities/… (bueno saber) tiene documentos mucho más claros aquí.
Destiny Architect

77
Parece que se requieren comillas cuando usa varias expresiones, por ejemplo, [ -z "" -a -z ${var+x} ]obtienebash: [: argument expected en bash 4.3-ubuntu (pero no, por ejemplo, zsh). Realmente no me gusta la respuesta usando una sintaxis que funciona en algunos casos.
FauxFaux

41
Usar un simple [ -z $var ]no es más "incorrecto" que omitir comillas. De cualquier manera, estás haciendo suposiciones sobre tu aporte. Si está bien tratar una cadena vacía como no establecida, [ -z $var ]es todo lo que necesita.
nshew

910

Para verificar la variable de cadena no nula / no nula, es decir, si está establecida, use

if [ -n "$1" ]

Es lo contrario de -z. Me encuentro usando -nmás de -z.

Lo usarías como:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi

86
Por lo general, prefiero [[ ]]sobre [ ], ya que [[ ]]es más poderoso y causa menos problemas en ciertas situaciones (vea esta pregunta para obtener una explicación de la diferencia entre los dos). La pregunta específicamente pide una solución bash y no menciona ningún requisito de portabilidad.
Flujo

18
La pregunta es cómo se puede ver si se establece una variable . Entonces no puede suponer que la variable está establecida.
Hola

77
Estoy de acuerdo, []funciona mejor especialmente para los scripts de shell (no bash).
Hengjie


63
Tenga en cuenta que -nes la prueba predeterminada, por lo que es más simple [ "$1" ]o [[ $1 ]]funciona también. También tenga en cuenta que con las [[ ]] citas es innecesario .
TNE

479

Aquí le mostramos cómo probar si un parámetro no está configurado , está vacío ("Nulo") o está configurado con un valor :

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |       parameter      |     parameter   |    parameter    |
|   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word}  | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word     | assign word     |
| ${parameter=word}  | substitute parameter | substitute null | assign word     |
| ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
| ${parameter?word}  | substitute parameter | substitute null | error, exit     |
| ${parameter:+word} | substitute word      | substitute null | substitute null |
| ${parameter+word}  | substitute word      | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+

Fuente: POSIX: Expansión de parámetros :

En todos los casos que se muestran con "sustituto", la expresión se reemplaza con el valor mostrado. En todos los casos que se muestran con "asignar", al parámetro se le asigna ese valor, que también reemplaza la expresión.

Para mostrar esto en acción:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  FOO="world"         |     FOO=""      |    unset FOO    |
|   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello}      | world                | hello           | hello           |
| ${FOO-hello}       | world                | ""              | hello           |
| ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
| ${FOO=hello}       | world                | ""              | FOO=hello       |
| ${FOO:?hello}      | world                | error, exit     | error, exit     |
| ${FOO?hello}       | world                | ""              | error, exit     |
| ${FOO:+hello}      | hello                | ""              | ""              |
| ${FOO+hello}       | hello                | hello           | ""              |
+--------------------+----------------------+-----------------+-----------------+

55
@HelloGoodbye Sí, funciona: set foo; echo ${1:-set but null or unset}echos "foo"; set --; echo ${1:-set but null or unset}ecos establecidos pero nulos ...
Jens

44
@HelloGoodbye Los parámetros posicionales se pueden establecer con, uh, set:-)
Jens

95
Esta respuesta es muy confusa. ¿Algún ejemplo práctico sobre cómo usar esta tabla?
Ben Davis

12
@BenDavis Los detalles se explican en el enlace al estándar POSIX, que hace referencia al capítulo del que se toma la tabla. Una construcción que uso en casi cualquier script que escribo es : ${FOOBAR:=/etc/foobar.conf}establecer un valor predeterminado para una variable si no está establecida o es nula.
Jens

1
parameteres cualquier nombre de variable. wordes una cadena a sustituir según la sintaxis de la tabla que está utilizando y si la variable $parameterestá establecida, establecida pero nula o sin establecer. ¡No para complicar las cosas, sino wordque también puede incluir variables! Esto puede ser muy útil para pasar argumentos opcionales separados por un carácter. Por ejemplo: ${parameter:+,${parameter}}genera una coma separada ,$parametersi está establecida pero no es nula. En este caso, wordes,${parameter}
TrinitronX

260

Si bien la mayoría de las técnicas indicadas aquí son correctas, bash 4.2 admite una prueba real de la presencia de una variable ( man bash ), en lugar de probar el valor de la variable.

[[ -v foo ]]; echo $?
# 1

foo=bar
[[ -v foo ]]; echo $?
# 0

foo=""
[[ -v foo ]]; echo $?
# 0

Notablemente, este enfoque no causará un error cuando se usa para verificar una variable no establecida en set -u/ set -o nounsetmode, a diferencia de muchos otros enfoques, como el uso [ -z.


99
En bash 4.1.2, independientemente de si la variable está configurada, [[ -v aaa ]]; echo $?==>-bash: conditional binary operator expected -bash: syntax error near 'aaa'
Dan

32
El argumento '-v' para la construcción 'test' se agregó en bash 4.2.
Ti Strga

19
Se agregó el argumento -v como complemento de la opción de shell -u (conjunto de sustantivos). Con 'nounset' activado (set -u), el shell devolverá un error y finalizará, si no es interactivo. $ # fue bueno para verificar los parámetros posicionales. Sin embargo, las variables con nombre se necesitaban de otra manera, además del clobbering, para identificarlas como no establecidas. Es lamentable que esta característica haya llegado tan tarde porque muchos están obligados a ser compatibles con versiones anteriores, en cuyo caso no pueden usarla. La única otra opción es usar trucos o 'hacks' para evitarlo como se muestra arriba, pero es muy poco elegante.
osirisgothra

2
Tenga en cuenta que esto no funciona para las variables que se declaran pero no se establecen. por ejemplodeclare -a foo
miken32

17
Es por eso que sigo leyendo después de la respuesta aceptada / mejor votada.
Joe

176

Hay muchas maneras de hacer esto, siendo las siguientes una de ellas:

if [ -z "$1" ]

Esto tiene éxito si $ 1 es nulo o no está establecido


46
Hay una diferencia entre un parámetro no establecido y un parámetro con un valor nulo.
chepner

21
Solo quiero ser pedante y señalar que esta es la respuesta opuesta a lo que plantea la pregunta. Las preguntas preguntan si la variable está establecida, no si la variable no está establecida.
Philluminati

47
[[ ]]no es más que una trampa de portabilidad. if [ -n "$1" ]debería usarse aquí.
Jens

73
Esta respuesta es incorrecta. La pregunta pide una manera de saber si una variable está configurada, no si está configurada en un valor no vacío. Dado foo="", $footiene un valor, pero -zsimplemente informará que está vacío. Y -z $fooexplotará si tienes set -o nounset.
Keith Thompson

44
Depende de la definición de "variable establecida". Si desea verificar si la variable está establecida en una cadena distinta de cero , la respuesta de mbranning es correcta. Pero si desea verificar si la variable se declara pero no se inicializa (es decir foo=), la respuesta de Russel es correcta.
Flujo

77

Siempre encuentro que la tabla POSIX en la otra respuesta es lenta para asimilar, así que aquí está mi opinión:

   +----------------------+------------+-----------------------+-----------------------+
   |   if VARIABLE is:    |    set     |         empty         |        unset          |
   +----------------------+------------+-----------------------+-----------------------+
 - |  ${VARIABLE-default} | $VARIABLE  |          ""           |       "default"       |
 = |  ${VARIABLE=default} | $VARIABLE  |          ""           | $(VARIABLE="default") |
 ? |  ${VARIABLE?default} | $VARIABLE  |          ""           |       exit 127        |
 + |  ${VARIABLE+default} | "default"  |       "default"       |          ""           |
   +----------------------+------------+-----------------------+-----------------------+
:- | ${VARIABLE:-default} | $VARIABLE  |       "default"       |       "default"       |
:= | ${VARIABLE:=default} | $VARIABLE  | $(VARIABLE="default") | $(VARIABLE="default") |
:? | ${VARIABLE:?default} | $VARIABLE  |       exit 127        |       exit 127        |
:+ | ${VARIABLE:+default} | "default"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

Tenga en cuenta que cada grupo (con y sin precedentes de colon) tiene el mismo conjunto y no definidas casos, por lo que lo único que difiere es la forma en la vacía se manejan casos.

Con los dos puntos anteriores, los casos vacíos y no definidos son idénticos, por lo que usaría aquellos donde sea posible (es decir :=, usar , no solo= , porque el caso vacío es incompatible).

Encabezados:

  • establecer medios VARIABLEno está vacío (VARIABLE="something" )
  • medios vacíosVARIABLE está vacío / nulo (VARIABLE="" )
  • los medios VARIABLEno establecidos no existen ( unset VARIABLE)

Valores:

  • $VARIABLE significa que el resultado es el valor original de la variable.
  • "default" significa que el resultado fue la cadena de reemplazo proporcionada.
  • "" significa que el resultado es nulo (una cadena vacía).
  • exit 127 significa que el script deja de ejecutarse con el código de salida 127.
  • $(VARIABLE="default")significa que el resultado es el valor original de la variable y la cadena de reemplazo proporcionada se asigna a la variable para uso futuro.

1
Solucionó solo los que son errores de codificación reales (instancias de indirección no deseada al trabajar con variables). Hice una edición para corregir algunos casos más en los que el dólar marca la diferencia al interpretar la descripción. Por cierto, todas las variables de shell (con la excepción de las matrices) son variables de cadena; Puede suceder que contengan una cadena que se pueda interpretar como un número. Hablar de cuerdas solo empaña el mensaje. Las citas no tienen nada que ver con las cadenas, son solo una alternativa para escapar. VARIABLE=""podría escribirse como VARIABLE=. Aún así, el primero es más legible.
Palec

Gracias por la tabla, es muy útil, sin embargo, estoy tratando de que la secuencia de comandos salga si está desarmado o vacío. Pero el código de salida que obtengo es 1, no 127.
Astronauta

64

Para ver si una variable no está vacía, uso

if [[ $var ]]; then ...       # `$var' expands to a nonempty string

Lo contrario prueba si una variable está desarmada o vacía:

if [[ ! $var ]]; then ...     # `$var' expands to the empty string (set or not)

Para ver si se establece una variable (vacía o no vacía), uso

if [[ ${var+x} ]]; then ...   # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ...     # Parameter 1 exists (empty or nonempty)

Lo contrario prueba si una variable no está establecida:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments

Yo también he estado usando esto por un tiempo; solo de manera reducida:[ $isMac ] && param="--enable-shared"

@Bhaskar Creo que es porque esta respuesta ya proporcionó esencialmente la misma respuesta (se usa ${variable+x}para obtener xiff $variable) casi medio año antes. También tiene muchos más detalles que explican por qué es correcto.
Mark Haferkamp

pero ${variable+x}es menos legible (y obvio)
knocte

35

En una versión moderna de Bash (4.2 o posterior, creo; no estoy seguro), intentaría esto:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
then
    echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
    echo "Variable is set to an empty string"
else
    echo "Variable is set to some string"
fi

55
También tenga en cuenta que [ -v "$VARNAME" ]no es incorrecto, sino que simplemente realiza una prueba diferente. Supongamos VARNAME=foo; luego comprueba si hay una variable llamada fooque se establece.
chepner

55
Nota para aquellos que desean portabilidad, -vno es compatible con POSIX
kzh

Si se obtiene [: -v: unexpected operator, hay que asegurarse de usar en bashlugar de sh.
koppor

25
if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

Aunque para los argumentos, normalmente es mejor probar $ #, que es el número de argumentos, en mi opinión.

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

1
La primera prueba es al revés; Creo que quieres [ "$1" != "" ](o [ -n "$1" ]) ...
Gordon Davisson

10
Esto fallará si $1se establece en la cadena vacía.
Hola

2
La segunda parte de la respuesta (parámetros de conteo) es una solución útil para la primera parte de la respuesta que no funciona cuando $1se establece en una cadena vacía.
Mark Berry

Esto fallará si set -o nounsetestá activado.
Flimm

21

Nota

Estoy dando una respuesta muy centrada en Bash debido a la bashetiqueta.

Respuesta corta

Siempre que solo se trate de variables con nombre en Bash, esta función siempre debería decirle si la variable se ha configurado, incluso si se trata de una matriz vacía.

variable-is-set() {
    declare -p "$1" &>/dev/null
}

Por que esto funciona

En Bash (al menos desde 3.0), si vares una variable declarada / establecida, declare -p vargenera un declarecomando que establecería la variable varsea ​​cual sea su tipo y valor actuales, y devuelve el código de estado 0(éxito). Si varno está declarado, declare -p varenvía un mensaje de error stderry devuelve el código de estado 1. Usando &>/dev/null, redirige tanto regular stdoutcomostderr salida a/dev/null , nunca se verá, y sin cambiar el código de estado. Por lo tanto, la función solo devuelve el código de estado.

¿Por qué otros métodos (a veces) fallan en Bash

  • [ -n "$var" ]: Esto solo comprueba si ${var[0]}no está vacío. (En Bash, $vares lo mismo que ${var[0]}).
  • [ -n "${var+x}" ]: Esto solo verifica si ${var[0]}está configurado.
  • [ "${#var[@]}" != 0 ]: Esto solo comprueba si se establece al menos un índice de $var.

Cuando este método falla en Bash

Esto sólo funciona para las variables con nombre (incluidos $_), no ciertas variables especiales ( $!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ..., y ninguno de los que puede haber olvidado). Como ninguno de estos son matrices, el estilo POSIX [ -n "${var+x}" ]funciona para todas estas variables especiales. Pero tenga cuidado de incluirlo en una función ya que muchas variables especiales cambian los valores / existencia cuando se llaman funciones.

Nota de compatibilidad de shell

Si su script tiene matrices y está tratando de hacerlo compatible con la mayor cantidad posible de shells, entonces considere usar en typeset -plugar de declare -p. He leído que ksh solo es compatible con el primero, pero no he podido probar esto. Sé que Bash 3.0+ y Zsh 5.5.1 son compatibles con ambos typeset -py declare -psolo difieren en cuál es una alternativa para el otro. Pero no he probado las diferencias más allá de esas dos palabras clave, y no he probado otros shells.

Si necesita que su script sea compatible con POSIX sh, no puede usar matrices. Sin matrices, [ -n "{$var+x}" ]funciona.

Código de comparación para diferentes métodos en Bash

Esta función desarma la variable var, evals el código pasado, ejecuta pruebas para determinar si varestá establecida por el evalcódigo d, y finalmente muestra los códigos de estado resultantes para las diferentes pruebas.

Estoy omitiendo test -v var, [ -v var ]y [[ -v var ]]porque producen resultados idénticos al estándar POSIX [ -n "${var+x}" ], al tiempo que requieren Bash 4.2+. También estoy omitiendo typeset -pporque es lo mismo que declare -pen los shells que he probado (Bash 3.0 a 5.0 y Zsh 5.5.1).

is-var-set-after() {
    # Set var by passed expression.
    unset var
    eval "$1"

    # Run the tests, in increasing order of accuracy.
    [ -n "$var" ] # (index 0 of) var is nonempty
    nonempty=$?
    [ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
    plus=$?
    [ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
    count=$?
    declare -p var &>/dev/null # var has been declared (any type)
    declared=$?

    # Show test results.
    printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}

Código de caso de prueba

Tenga en cuenta que los resultados de la prueba pueden ser inesperados debido a que Bash trata los índices de matriz no numéricos como "0" si la variable no se ha declarado como una matriz asociativa. Además, las matrices asociativas solo son válidas en Bash 4.0+.

# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo"                        #  0  0  0  0
is-var-set-after "var=(foo)"                      #  0  0  0  0
is-var-set-after "var=([0]=foo)"                  #  0  0  0  0
is-var-set-after "var=([x]=foo)"                  #  0  0  0  0
is-var-set-after "var=([y]=bar [x]=foo)"          #  0  0  0  0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''"                         #  1  0  0  0
is-var-set-after "var=([0]='')"                   #  1  0  0  0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')"                   #  1  1  0  0
is-var-set-after "var=([1]=foo)"                  #  1  1  0  0
is-var-set-after "declare -A var; var=([x]=foo)"  #  1  1  0  0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()"                         #  1  1  1  0
is-var-set-after "declare -a var"                 #  1  1  1  0
is-var-set-after "declare -A var"                 #  1  1  1  0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var"                      #  1  1  1  1

Prueba de salida

Los mnemónicos de ensayo en la fila de cabecera corresponden a [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ], y declare -p var, respectivamente.

                         test: -n +x #@ -p
                      var=foo:  0  0  0  0
                    var=(foo):  0  0  0  0
                var=([0]=foo):  0  0  0  0
                var=([x]=foo):  0  0  0  0
        var=([y]=bar [x]=foo):  0  0  0  0
                       var='':  1  0  0  0
                 var=([0]=''):  1  0  0  0
                 var=([1]=''):  1  1  0  0
                var=([1]=foo):  1  1  0  0
declare -A var; var=([x]=foo):  1  1  0  0
                       var=():  1  1  1  0
               declare -a var:  1  1  1  0
               declare -A var:  1  1  1  0
                    unset var:  1  1  1  1

Resumen

  • declare -p var &>/dev/null es (100%?) confiable para probar variables con nombre en Bash desde al menos 3.0.
  • [ -n "${var+x}" ] es confiable en situaciones compatibles con POSIX, pero no puede manejar matrices.
  • Existen otras pruebas para verificar si una variable no está vacía y para verificar las variables declaradas en otros shells. Pero estas pruebas no son adecuadas para las secuencias de comandos Bash ni POSIX.

3
De las respuestas que probé, esta ( declare -p var &>/dev/null) es la ÚNICA que funciona. ¡GRACIAS!
user1387866

2
@ mark-haferkamp Intenté editar su respuesta para agregar el "/" precedente crítico a su código de función para que se lea, ... &> /dev/nullpero SO no me permitió hacer una edición de un solo carácter 🙄 (debe ser> 6 caracteres, incluso intenté agregar espacios en blanco Pero no funcionó). Además, puede valer la pena cambiar la función para cambiar $1una muestra llamada variable (p OUTPUT_DIR. Ej. ) Ya que, como señaló, las variables especiales como $1no funcionan aquí. De todos modos, es una edición crítica, de lo contrario, el código está buscando crear un archivo llamado nullen un directorio relativo llamado dev. Por cierto, ¡gran respuesta!
joehanna

Además, usar el nombre de la variable sin el prefijo $ también funciona: declare -p PATH &> /dev/nulldevuelve 0 donde como declare -p ASDFASDGFASKDF &> /dev/nulldevuelve 1. (en bash 5.0.11 (1) -release)
joehanna

1
¡Buen trabajo! 1 palabra de precaución: declare vardeclara var una variable pero no la establece en términos de set -o nounset: Prueba condeclare foo bar=; set -o nounset; echo $bar; echo $foo
xebeche

@joehanna ¡Gracias por descubrir lo que falta /! Lo arreglé, pero creo que la lógica es lo suficientemente confusa como para garantizar una función, especialmente a la luz del comentario de @ xebeche. @xebeche Eso es un inconveniente para mi respuesta ... Parece que tendré que intentar algo como declare -p "$1" | grep =o test -v "$1".
Mark Haferkamp

19

Para aquellos que buscan verificar si están desarmados o vacíos cuando están en un script con set -u:

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi

La [ -z "$var" ]comprobación regular fallará con var; unbound variableif set -upero se [ -z "${var-}" ] expande a una cadena vacía si no varse establece sin fallar.


Te falta un colon. Debería ser ${var:-}, no ${var-}. Pero en Bash, puedo confirmar que funciona incluso sin el colon.
Palec

3
${var:-}y ${var-}significan cosas diferentes. ${var:-}se expandirá para vaciar cadena si vardesarmar o nula y ${var-}se expandirá a sólo si desarmar cadena vacía.
RubenLaguna

Acabo de aprender algo nuevo, gracias! Omitir los dos puntos da como resultado una prueba solo para un parámetro que no está establecido. Dicho de otra manera, si se incluyen los dos puntos, el operador comprueba 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. Aquí no hay diferencia, pero es bueno saberlo.
Palec

17

Desea salir si no está configurado

Esto funcionó para mí. Quería que mi script salga con un mensaje de error si no se configuró un parámetro.

#!/usr/bin/env bash

set -o errexit

# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"

Esto vuelve con un error cuando se ejecuta

peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument

Marque solo, sin salida - Vacío y desarmado son inválidos

Pruebe esta opción si solo desea verificar si el valor establecido = VÁLIDO o unset / empty = INVALID.

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

O, incluso pruebas cortas ;-)

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"

Verificar solo, sin salida - Solo vacío es inválido

Y esta es la respuesta a la pregunta. Use esto si solo desea verificar si el valor establecido / vacío = VÁLIDO o sin establecer = NO VÁLIDO.

NOTA, el "1" en "..- 1}" es insignificante, puede ser cualquier cosa (como x)

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Pruebas cortas

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"

Dedico esta respuesta a @ mklement0 (comentarios) que me retó a responder la pregunta con precisión.

Referencia http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02


15

Para verificar si una variable está configurada con un valor no vacío, use [ -n "$x" ], como ya lo han indicado otros.

La mayoría de las veces, es una buena idea tratar una variable que tiene un valor vacío de la misma manera que una variable que no está configurada. Pero puede distinguir los dos si lo necesita: [ -n "${x+set}" ](se "${x+set}"expande a setsi xestá configurado y a la cadena vacía si no xestá configurado).

Para verificar si se ha pasado un parámetro, pruebe $#, que es el número de parámetros pasados ​​a la función (o al script, cuando no está en una función) (vea la respuesta de Paul ).


14

Lea la sección "Expansión de parámetros" de la bashpágina del manual. La expansión de parámetros no proporciona una prueba general para establecer una variable, pero hay varias cosas que puede hacer con un parámetro si no está configurado.

Por ejemplo:

function a {
    first_arg=${1-foo}
    # rest of the function
}

se establecerá first_argigual a $1si está asignado, de lo contrario, utiliza el valor "foo". Si aabsolutamente debe tomar un solo parámetro, y no existe un buen valor predeterminado, puede salir con un mensaje de error cuando no se proporciona ningún parámetro:

function a {
    : ${1?a must take a single argument}
    # rest of the function
}

(Tenga en cuenta el uso de :como un comando nulo, que solo expande los valores de sus argumentos. No queremos hacer nada $1en este ejemplo, simplemente salga si no está configurado)


1
Estoy desconcertado de que ninguna de las otras respuestas mencione lo simple y elegante : ${var?message}.
tripleee

¿De dónde sacaste esto? ¡Maravilloso! ¡Funciona justamente! ¡Y es corto y elegante! ¡Gracias!
Roger el

13

En bash puedes usar -vdentro de la [[ ]]construcción:

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then
    SOMEVAR='hello'
fi

echo $SOMEVAR

8

Usar [[ -z "$var" ]]es la forma más fácil de saber si una variable se configuró o no, pero esa opción -zno distingue entre una variable no establecida y una variable establecida en una cadena vacía:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset

Es mejor verificarlo de acuerdo con el tipo de variable: variable env, parámetro o variable regular.

Para una variable env:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

Para un parámetro (por ejemplo, para verificar la existencia del parámetro $5):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

Para una variable regular (usando una función auxiliar, para hacerlo de una manera elegante):

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"

Notas:

  • $#: le da el número de parámetros posicionales.
  • declare -p: le da la definición de la variable pasada como parámetro. Si existe, devuelve 0, si no, devuelve 1 e imprime un mensaje de error.
  • &> /dev/null: suprime la salida de declare -psin afectar su código de retorno.

5

Las respuestas anteriores no funcionan cuando la opción Bash set -uestá habilitada. Además, no son dinámicos, por ejemplo, ¿cómo se define la variable de prueba con el nombre "dummy"? Prueba esto:

is_var_defined()
{
    if [ $# -ne 1 ]
    then
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    fi
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z \${$1:-} ]"
    return $?  # Pedantic.
}

Relacionado: En Bash, ¿cómo pruebo si una variable se define en modo "-u"?


1
Me gustaría saber por qué esta respuesta fue rechazada ... Funciona muy bien para mí.
LavaScornedOven

En Bash, se podría hacer lo mismo sin una eval: el cambio eval "[ ! -z \${$1:-} ]"a la evaluación indirecta: [ ! -z ${!1:-} ];.

@BinaryZebra: idea interesante. Te sugiero que publiques como una respuesta separada.
kevinarpe

El uso de "eval" hace que esta solución sea vulnerable a la inyección de código: $ is_var_defined 'x}]; echo "gotcha"> & 2; # 'gotcha
tetsujin


4

Mi forma preferida es esta:

$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

Básicamente, si se establece una variable, se convierte en "una negación de lo resultante false" (lo que será true= "se establece").

Y, si no está establecido, se convertirá en "una negación de lo resultante true" (como se evalúa el resultado vacío true) (así terminará como false= = NO establecido ").



1

En un shell puedes usar el -z operador que es True si la longitud de la cadena es cero.

Una línea simple para establecer el valor predeterminado MY_VARsi no está configurado; de lo contrario, opcionalmente puede mostrar el mensaje:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."

0
if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2
fi

if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2
fi

$1, $2y hasta $Nsiempre está configurado. Lo siento, esto es un fracaso.

Lo intenté y no funcionó, tal vez mi versión bash carece de soporte para eso. Idk, lo siento si me equivoco. :)

0

Encontré un código (mucho) mejor para hacer esto si desea verificar cualquier cosa $@.

si [[$ 1 = ""]]
entonces
  echo '$ 1 está en blanco'
más
  echo '$ 1 está lleno'
fi

¿Por qué todo esto? Todo en $@existe en Bash, pero por defecto de blanco, así test -zy test -npodría ayudarte.

Actualización: También puede contar el número de caracteres en un parámetro.

si [$ {# 1} = 0]
entonces
  echo '$ 1 está en blanco'
más
  echo '$ 1 está lleno'
fi

Lo sé, pero eso es todo. ¡Si algo está vacío, obtendrá un error! Lo siento. : P

0
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi

11
Bienvenido a StackOverflow. Si bien su respuesta es apreciada, es mejor dar más que solo código. ¿Podría agregar un razonamiento a su respuesta o una pequeña explicación? Vea esta página para otras ideas sobre cómo expandir su respuesta.
Celeo

0

Esto es lo que uso todos los días:

#
# Check if a variable is set
#   param1  name of the variable
#
function is_set()
{
    [[ -n "${1}" ]] && test -n "$(eval "echo "\${${1}+x}"")"
}

Esto funciona bien en Linux y Solaris hasta bash 3.0.

bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ mavar=""
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?
1

1
Desde Bash 2.0+ la expansión indirecta está disponible. Puede reemplazar el largo y complejo (con una evaluación también incluida) test -n "$(eval "echo "\${${1}+x}"")"por el equivalente:[[ ${!1+x} ]]

0

Me gustan las funciones auxiliares para ocultar los detalles crudos de bash. En este caso, hacerlo agrega aún más crudeza (oculta):

# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
{
  local varname="$1"
  ! [ -z ${!varname+x} ]
}

Como tuve errores por primera vez en esta implementación (inspirada en las respuestas de Jens y Lionel), se me ocurrió una solución diferente:

# Ask for the properties of the variable - fails if not declared
function IsDeclared()
{
  declare -p $1 &>/dev/null
}

Me parece más directo, más tímido y más fácil de entender / recordar. El caso de prueba muestra que es equivalente:

function main()
{
  declare -i xyz
  local foo
  local bar=
  local baz=''

  IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
  IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
  IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
  IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"

  IsDeclared xyz; echo "IsDeclared xyz: $?"
  IsDeclared foo; echo "IsDeclared foo: $?"
  IsDeclared bar; echo "IsDeclared bar: $?"
  IsDeclared baz; echo "IsDeclared baz: $?"
}

main

El caso de prueba también muestra que local varno NO declaran var (a menos que seguido por '='). Durante bastante tiempo pensé que declaraba las variables de esta manera, solo para descubrir ahora que simplemente expresé mi intención ... Supongo que no funciona.

IsDeclared_Tricky xyz: 1
foo IsDeclared_Tricky: 1
IsDeclared_Tricky bar: 0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
foo IsDeclared: 1
bar IsDeclared: 0
baz IsDeclared: 0

BONIFICACIÓN: caso de uso

Principalmente uso esta prueba para dar (y devolver) parámetros a las funciones de una manera algo "elegante" y segura (casi como una interfaz ...):

#auxiliary functions
function die()
{
  echo "Error: $1"; exit 1
}

function assertVariableDeclared()
{
  IsDeclared "$1" || die "variable not declared: $1"
}

function expectVariables()
{
  while (( $# > 0 )); do
    assertVariableDeclared $1; shift
  done
}

# actual example
function exampleFunction()
{
  expectVariables inputStr outputStr
  outputStr="$inputStr world!"
}

function bonus()
{
  local inputStr='Hello'
  local outputStr= # remove this to trigger error
  exampleFunction
  echo $outputStr
}

bonus

Si se llama con todas las variables requeridas declaradas:

¡Hola Mundo!

más:

Error: variable no declarada: outputStr


0

Después de leer todas las respuestas, esto también funciona:

if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi
echo "SOME_VAR=$SOME_VAR"

si no pone en SOME_VARlugar de lo que tengo $SOME_VAR, lo establecerá en un valor vacío; $Es necesario que esto funcione.


no funcionará cuando tenga set -uen efecto lo que imprimirá el unboud variableerror
Michael Heuberger

0

Resumen

  • Use test -n "${var-}"para verificar si la variable no está vacía (y por lo tanto debe definirse / establecerse también)
  • Use test ! -z "${var+}"para verificar si la variable está definida / establecida (incluso si está vacía)

Tenga en cuenta que el primer caso de uso es mucho más común en los scripts de shell y esto es lo que generalmente querrá usar.

Notas

  • Esta solución debería funcionar en todos los shells POSIX (sh, bash, zsh, ksh, dash)
  • Algunas de las otras respuestas a esta pregunta son correctas, pero pueden ser confusas para las personas sin experiencia en la creación de scripts de shell, por lo que quería proporcionar una respuesta TLDR que sea menos confusa para esas personas.

Explicación

Para comprender cómo funciona esta solución, debe comprender el comando POSIXtest y la expansión ( especificación ) del parámetro de shell POSIX , así que cubramos los conceptos básicos necesarios para comprender la respuesta.

El comando de prueba evalúa una expresión y devuelve verdadero o falso (a través de su estado de salida). El operador -ndevuelve verdadero si el operando es una cadena no vacía. Entonces, por ejemplo, test -n "a"devuelve verdadero, mientras que test -n ""devuelve falso. Ahora, para verificar si una variable no está vacía (lo que significa que debe definirse), puede usarla test -n "$var". Sin embargo, algunos scripts de shell tienen una opción set ( set -u) que hace que cualquier referencia a variables indefinidas emita un error, por lo que si la variable varno está definida, la expresión $acausará un error. Para manejar este caso correctamente, debe usar la expansión de variables, que le indicará al shell que reemplace la variable con una cadena alternativa si no está definida, evitando el error mencionado anteriormente.

La expansión de la variable ${var-}significa: si la variable varno está definida (también llamada "no establecida"), reemplácela con una cadena vacía. Por test -n "${var-}"lo tanto , devolverá verdadero si $varno está vacío, que es casi siempre lo que desea verificar en los scripts de shell. La verificación inversa, si $varno está definida o no está vacía, sería test -z "${var-}".

Ahora al segundo caso de uso: verificar si la variable varestá definida, ya sea vacía o no. Este es un caso de uso menos común y un poco más complejo, y le recomendaría que lea la excelente respuesta de Lionels para comprenderla mejor.


-1

Para verificar si una var está configurada o no

var=""; [[ $var ]] && echo "set" || echo "not set"

3
Esto fallará si $varse establece en la cadena vacía.
Hola

-1

Si desea probar que una variable está vinculada o no, esto funciona bien, incluso después de haber activado la opción de sustantivo:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
else
    # variable is unbound
fi

Creo que quieres decir set -o nounset, no set -o noun set. Esto solo funciona para variables que han sido exporteditadas. También cambia su configuración de una manera que es difícil de deshacer.
Keith Thompson
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.