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

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

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

function a { # if $1 is set ? } 

(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ámetro que se evalúa en nada si var está desarmada y sustituye la cadena x contrario.

Quotes Digression

Las citas se pueden omitir (por lo que podemos decir ${var+x} lugar de "${var+x}" ) porque esta syntax y uso garantiza que esto solo se ampliará a algo que no requiera comillas (ya que se expande a x (que no contiene ningún salto de palabra, por lo que no necesita comillas), o para nada (lo que da como resultado [ -z ] , que convenientemente evalúa el mismo valor (verdadero) que [ -z "" ] también)).

Sin embargo, aunque las citas se pueden omitir de manera segura, y no fue inmediatamente obvio para todos (ni siquiera era aparente para el primer autor de esta explicación de citas quién también es un gran codificador de Bash), a veces sería mejor escribir la solución con comillas como [ -z "${var+x}" ] , al muy pequeño 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 se pueden omitir con seguridad.

(A menudo) La manera incorrecta

 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 está desarmada y una variable que está configurada en la cadena vacía. Es decir, si var='' , la solución anterior dará como resultado “var está en blanco”.

La distinción entre unset y “set to the empty string” es esencial en situaciones donde el usuario tiene que especificar una extensión, o una lista adicional de propiedades, y que no especificarlas tiene un valor predeterminado 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.

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

 if [ -n "$1" ] 

Es lo opuesto a -z . Me encuentro usando -n más que -z .

A continuación, se indica cómo probar si un parámetro está desactivado o vacío (“Nulo”) o configurado con un valor :

 +--------------------+----------------------+-----------------+-----------------+ | | parameter | parameter | parameter | | | 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 que se muestra. En todos los casos que se muestran con “assign”, el parámetro se asigna a ese valor, que también reemplaza la expresión.

Hay muchas maneras de hacer esto, siendo el siguiente uno de ellos:

 if [ -z "$1" ] 

Esto tiene éxito si $ 1 es nulo o desarmado

Si bien la mayoría de las técnicas indicadas aquí son correctas, bash 4.2 admite una prueba real para 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 

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

 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 una variable está configurada (vacía o no), uso

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

Lo opuesto prueba si una variable no está configurada:

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

Siempre encuentro lenta la tabla POSIX en la otra respuesta , 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 dos puntos anteriores) tiene los mismos casos configurados y no configurados , por lo que lo único que difiere es cómo se manejan los casos vacíos .

Con los dos puntos anteriores, los casos vacíos y no configurados son idénticos, así que los usaría cuando sea posible (es decir, use := , no solo = , porque el caso vacío es inconsistente).

Encabezados:

  • set significa que VARIABLE no está vacío ( VARIABLE="something" )
  • vacío significa que VARIABLE está vacío / nulo ( VARIABLE="" )
  • unset significa que VARIABLE no existe ( 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 provista.
  • "" 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.

En una versión moderna de Bash (4.2 o posterior, creo, no estoy seguro), probarí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 
 if [ "$1" != "" ]; then echo \$1 is set else echo \$1 is not set fi 

Aunque para 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 

Lea la sección “Expansión de parámetros” de la página de manual de bash . La expansión de parámetros no proporciona una prueba general para establecer una variable, pero hay varias cosas que puede hacer a un parámetro si no está establecido.

Por ejemplo:

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

establecerá first_arg igual a $1 si está asignado; de lo contrario, usará el valor “foo”. Si absolutamente debe tomar un solo parámetro, y no existe un buen valor predeterminado, puede salir con un mensaje de error cuando no se da 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 simplemente expande los valores de sus argumentos. No queremos hacer nada con $1 en este ejemplo, solo salga si no está configurado)

Quieres salir si está desarmado

Esto funcionó para mí. Quería que mi script saliera con un mensaje de error si no se configuraba 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 retorna 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 

Verifique solo, no hay salida – Vacío y Desactivado son INVÁLIDOS

Pruebe esta opción si solo desea verificar si el valor establecido es = VÁLIDO o no configurado / vacío = NO VÁLIDO.

 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, no salir – Solo vaciar es NO VÁLIDO

Y esta es la respuesta a la pregunta. Úselo si solo desea verificar si el valor establecido / vacío = VALIDO o no establecido = 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 desafió a responder la pregunta con precisión.

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

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

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 está desarmada. Pero puede distinguir los dos si es necesario: [ -n "${x+set}" ] ( "${x+set}" expande para set si x está establecido y para la cadena vacía si x está desarmado).

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 ).

En bash puedes usar -v dentro del [[ ]] incorporado:

 #! /bin/bash -u if [[ ! -v SOMEVAR ]]; then SOMEVAR='hello' fi echo $SOMEVAR 

Para aquellos que buscan verificar si está desarmado o vacío cuando está en una secuencia de comandos con el set -u :

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

La comprobación regular [ -z "$var" ] fallará con var; unbound variable var; unbound variable si se set -u pero [ -z "${var-}" ] expande a cadena vacía si var se desarma sin fallar.

Tu puedes hacer:

 function a { if [ ! -z "$1" ]; then echo '$1 is set' fi } 

Las respuestas anteriores no funcionan cuando la opción Bash se set -u está habilitada. Además, no son dynamics, por ejemplo, cómo se prueba es variable con el nombre “ficticio” se define? Prueba esto:

 is_var_defined() { if [ $# -ne 1 ] then echo "Expected exactly one argument: variable name as string, eg, '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 está definida en el modo “-u”

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

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

Lo mejor es verificarlo según 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 return $? } declare_var "var_name" && echo "Set" || echo "Unset" 

Notas:

  • $# : le dice 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.
  • $? : le proporciona el código de estado del último comando ejecutado.

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

Un simple trazador de líneas para establecer MY_VAR predeterminado si 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." 

Mi forma preferida es esta:

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

Entonces, básicamente, si se establece una variable, se convierte en “una negación del false resultante” (lo que será true = “se establece”).

Y, si no está configurado, se convertirá en “una negación del true resultado” (como el resultado vacío se evalúa como true ) (por lo que terminará como false = “NO establecido”).

 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 

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

 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 está en blanco, así que test -z y test -n no podrían ayudarte.

Actualización: también puede contar la cantidad de caracteres en un parámetro.

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

O

 (( ${#foo} )) 

O

 let ${#foo} 

O

 declare -p foo 
 if [[ ${!xx[@]} ]] ; then echo xx is defined; fi 

Siempre uso este, basado en el hecho de que parece fácil de ser entendido por cualquiera que vea el código por primera vez:

 if [ "$variable" = "" ] then echo "Variable X is empty" fi 

Y, si quiere comprobar si no está vacío;

 if [ ! "$variable" = "" ] then echo "Variable X is not empty" fi 

Eso es.

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 

Si var puede ser una matriz, la sustitución del parámetro [ -z "${var+x}" ] es incorrecta. Para estar realmente seguro en Bash necesitas usar una syntax de matriz como [ "${#var[@]}" = 0 ] , como se muestra a continuación.

 is-var-set () { results="\${var+x}=${var+x}\t\${#var[@]}=${#var[@]}" if [ -z "${var+x}" ] && [ "${#var[@]}" = 0 ]; then echo -e "$1: var's unset.\t$results" elif [ -n "${var+x}" ] && [ "${#var[@]}" != 0 ]; then echo -e "$1: var is set. \t$results" else echo -e "$1: Is var set? \t$results" fi unset var # so we don't have to do it everywhere else } 

En casi todos los casos, están de acuerdo. La única situación que he encontrado donde el método de matriz es más preciso es cuando la variable es una matriz no vacía con la posición 0 ajustada (por ejemplo, en las pruebas 7 y A adelante). Este desacuerdo viene de que $var es la abreviatura de ${var[0]} , por lo que [ -z "${var+x}" ] no está verificando toda la matriz.

Aquí están mis casos de prueba.

 unset var; is-var-set 1 # var unset var=''; is-var-set 2 # var[0] set to '' var=foo; is-var-set 3 # var[0] set to 'foo' var=(); is-var-set 4 # var unset (all indices) var=(foo); is-var-set 5 # var[0] set to 'foo' var=([0]=foo); is-var-set 6 # var[0] set to 'foo' var=([1]=foo); is-var-set 7 # var[0] unset, but var[1] set to 'foo' declare -a var; is-var-set 8 # var empty, but declared as an array declare -A var; is-var-set 9 # var empty, but declared as an associative array declare -A var # Because is-var-set() conveniently unsets it var=([xz]=foo); is-var-set A # var[xz] set to 'foo', but var's otherwise empty declare -a var # Demonstrate that Bash knows about var, even when there's declare -A var; is-var-set B # apparently no way to just _check_ its existence 

Aquí está la salida.

 1: var's unset. ${var+x}= ${#var[@]}=0 2: var is set. ${var+x}=x ${#var[@]}=1 3: var is set. ${var+x}=x ${#var[@]}=1 4: var's unset. ${var+x}= ${#var[@]}=0 5: var is set. ${var+x}=x ${#var[@]}=1 6: var is set. ${var+x}=x ${#var[@]}=1 7: Is var set? ${var+x}= ${#var[@]}=1 8: var's unset. ${var+x}= ${#var[@]}=0 9: var's unset. ${var+x}= ${#var[@]}=0 A: Is var set? ${var+x}= ${#var[@]}=1 ./foo.sh: line 26: declare: var: cannot convert indexed to associative array B: var's unset. ${var+x}= ${#var[@]}=0 

En sum:

  • ${var+x} syntax de expansión de parámetros ${var+x} funciona tan bien como la syntax de matriz ${#var[@]} en la mayoría de los casos, como la comprobación de parámetros para funciones. La única forma en que este caso podría romperse es si una versión futura de Bash agrega una forma de pasar arreglos a funciones sin convertir contenidos a argumentos individuales.
  • La syntax de array es necesaria para arreglos no vacíos (asociativos o no) con el elemento 0 unset.
  • Ninguna de las dos syntax explica lo que está sucediendo si declare -a var se ha utilizado una varilla sin asignar ni siquiera un valor nulo en algún lugar de la matriz. Bash todavía distingue el caso en algún lugar (como se vio en la prueba B anterior), por lo que esta respuesta no es infalible. Afortunadamente, Bash convierte las variables de entorno exportadas en cadenas al ejecutar un progtwig / script, por lo que cualquier problema con las variables declaradas pero no configuradas se incluirá en un solo script, al menos si no se obtienen otros scripts.

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

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

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} ] } 

Because I first had bugs in this implementation (inspired by the answers of Jens and Lionel), I came up with a different solution:

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

I find it to be more straight-forward, more bashy and easier to understand/remember. Test case shows it is equivalent:

 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 

The test case also shows that local var does NOT declare var (unless followed by ‘=’). For quite some time I thought i declared variables this way, just to discover now that i merely expressed my intention… It’s a no-op, i guess.

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

BONUS: usecase

I mostly use this test to give (and return) parameters to functions in a somewhat “elegant” and safe way (almost resembling an interface…):

 #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 

If called with all requires variables declared:

¡Hola Mundo!

más:

Error: variable not declared: outputStr

Functions to check if variable is declared/unset

including empty $array=()

The following functions test if the given name exists as a variable

 # 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;} } 
  • By first testing if the variable is (un)set, the call to declare can be avoided, if not necessary.
  • If however $1 contains the name of an empty $array=() , the call to declare would make sure we get the right result
  • There’s never much data passed to /dev/null as declare is only called if either the variable is unset or an empty array.

This functions would test as showed in the following conditions:

 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 

.

Para más detalles

and a test script see my answer to the question “How do I check if a variable exists in bash?” .

Remark: The similar usage of declare -p , as it is also shown by Peregring-lk ‘s answer , is truly coincidental. Otherwise I would of course have credited it!

 case "$1" in "") echo "blank";; *) echo "set" esac