2008-10-23 20 views
134

Si quiero comprobar si la cadena vacía que haría¿Cómo saber si una cadena no está definida en un script bash shell?

[ -z $mystr ] 

pero lo que si quiero comprobar si la variable se ha definido en absoluto? ¿O no hay distinción en los scripts bash?

+26

tenga la costumbre de usar [-z "$ mystr"] en lugar de [-z $ mystr] –

+0

cómo hacer lo inverso? Me refiero a cuando la cadena no es nula – flow

+1

@flow: ¿Qué pasa con '[-n" $ {VAR + x} "] && echo not null' – Lekensteyn

Respuesta

122

Creo que la respuesta que buscas está implícita (si no está indicada) en Vinko 's answer, aunque no está explicado simplemente. Para distinguir si VAR está asignada, pero vacía o no se establece, se puede utilizar:

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 

Es probable que pueda combinar las dos pruebas en la segunda línea en uno con:

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

Sin embargo, si se lee la documentación de Autoconf, encontrará que no recomiendan combinar términos con '-a' y recomiendan usar pruebas simples separadas combinadas con &&. No he encontrado un sistema donde haya un problema; eso no quiere decir que no solían existir (pero probablemente sean extremadamente raros actualmente, incluso si no fueran tan raros en el pasado distante).

Puede encontrar los detalles de estos y otros relacionados shell parameter expansions, el comando test or [ y conditional expressions en el manual de Bash.


poco me preguntaron por correo electrónico acerca de esta respuesta con la pregunta:

utiliza dos pruebas, y entiendo que la segunda también, pero no el primero. Más precisamente, no entiendo la necesidad de 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 

Feria pregunta - la respuesta es 'No, su alternativa más simple no hace lo mismo'.

Supongamos que escribo esto antes de la prueba:

VAR= 

Su prueba dirá "VAR no se ajusta en absoluto", pero la mía va a decir (por implicación, ya que se hace eco de nada) "VAR está asignada pero su el valor puede estar vacío ". Trate de 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, se establece la variable, sino que se establece en el valor vacío. Esta es la distinción que hacen las anotaciones ${VAR=value} y ${VAR:=value}. Ídem para ${VAR-value} y ${VAR:-value}, y ${VAR+value} y ${VAR:+value}, y así sucesivamente.


Como Gili señala en su answer, si ejecuta bash con la opción set -o nounset, entonces la respuesta básica anterior falla con unbound variable. Se soluciona fácilmente:

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 usted podría cancelar la opción set -o nounset con set +u (set -u equivalente a set -o nounset).

+7

El único problema que tengo con esta respuesta es que cumple su tarea de una manera bastante indirecta y poco clara. – Swiss

+3

Para aquellos que quieran buscar la descripción de lo que significa arriba en la página de manual de bash, busque la sección "Expansión de parámetros" y luego para este texto: "Cuando no se realiza la expansión de subcadena, utilizando los formularios documentados a continuación, bash tests para un parámetro que está desarmado o nulo ['nulo' significa la cadena vacía]. Omitir los dos puntos da como resultado una prueba solo para un parámetro que está desarmado (...) $ {parámetro: + palabra}: Usar valor alternativo. el parámetro es nulo o no está configurado, no se sustituye nada; de lo contrario, se sustituye la expansión de la palabra." –

+2

@Swiss Esto no es confuso ni indirecto, sino idiomático. Tal vez para un programador que no esté familiarizado con' $ {+} 'y' $ {-} 'no esté claro, pero la familiaridad con esos constructos es esencial para que uno sea un usuario competente del shell. –

31
~> 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 trabaja para las variables indefinidas también. Para distinguir entre un indefinido y uno definido, usaría las cosas enumeradas en here o, con explicaciones más claras, here.

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

palabra alternativo:

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

Valor por defecto:

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

Por supuesto que tendría que utilizar una de ellas de forma diferente, poniendo el valor que desea en lugar de 'valor por defecto' y el uso de la expansión directamente, si es apropiado.

+1

Pero quiero distinguir si la cadena es "" o no se ha definido alguna vez. ¿Es eso posible? – SetJmp

+1

esta respuesta * no * dice cómo distinguir entre esos dos casos; sigue el enlace bash faq proporciona más discusión. –

+1

Agregué que después de su comentario, Charles –

-3

establecimiento de llamada sin argumentos .. da salida a todos los VARs definidos ..
los últimos en la lista serían los definidos en el script ..
por lo que podría tubería de su salida a algo que podría imaginar qué cosas están definidas y qué no es

+2

No lo hice Voto abajo, pero es una especie de martillo romper un hueso bastante pequeño. –

+0

Realmente me gusta esta respuesta mejor que la respuesta aceptada. Está claro lo que se está haciendo en esta respuesta. La respuesta aceptada es débil como el infierno. – Swiss

+0

Parece que esta respuesta es más un efecto secundario de la implementación de "conjunto" que algo deseable. –

16

Guía de scripts avanzados bash, 10.2. La sustitución de parámetros:

  • $ {var + blahblah}: si se define var, 'blahblah' es sustituida por la expresión , de lo contrario nula es sustituido
  • $ {var-blahblah}: si se define var, es en sí mismo sustituido, de lo contrario 'blahblah' es
  • $ {var? blahblah}: si var está definido, está sustituido, de lo contrario la función existe con 'blahblah' como un mensaje de error.


basar su lógica del programa en si se ha definido el $ mystr variable o no, puede hacer lo siguiente:

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

ahora, si IsDefined = 0, entonces la variable era undefined, if isdefined = 1 la variable se definió

Esta forma de comprobar variables es mejor que la respuesta anterior porque es más elegante, legible, y si su shell bash se configuró para error en el uso de variables indefinidas (set -u), la secuencia de comandos terminará prematuramente.


Otras cosas útiles:

que tienen un valor por defecto de 7 asigna a $ mystr si era indefinido, y dejar intacto lo contrario:

mystr=${mystr- 7} 

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

: ${mystr? not defined} 

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

+0

No sabía acerca de la sintaxis para las variables BASH. Esa es una buena captura. – Swiss

+0

Esto funciona también con referencias de variables indirectas. Esto pasa: 'var =; varname = var;: $ {! varname? no definido}' y esto termina: 'varname = var;: $ {! varname? no definido}'. Pero es un buen hábito para usar 'set -u', que hace lo mismo mucho más fácil. – ceving

0

Esto es lo que creo que es una forma mucho más clara para comprobar si una variable se define:

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

utilizarlo como sigue:

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

Wri una función no es una mala idea; invocar 'grep' es horrible. Tenga en cuenta que 'bash' admite' $ {! Var_name} 'como una forma de obtener el valor de una variable cuyo nombre se especifica en' $ var_name', por lo que 'name = value; valor = 1; echo $ {name} $ {! name} 'produce' value 1'. –

1

otra opción: the "list array indices" expansion:

$ 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 comprobarlo 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

7

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" 
+0

¿Por qué las comillas? –

+0

Buenas prácticas en bash. A veces las rutas de archivos tienen espacios, o para proteger contra la inyección de comandos – k107

+1

Creo que con '$ {var + x}' no es necesario, excepto si se permite que las variables * nombres * tengan espacios en ellas. –

1

no derramar esta moto aún más, pero quería añadir

shopt -s -o nounset 

es algo que se podía añadir a la parte superior de un guión, que será de error si las variables no son declarados en cualquier parte en el guion El mensaje que vería es unbound variable, pero como otros lo mencionan, no detectará una cadena vacía o un valor nulo. Para asegurarse de que ningún 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 podría generar un error con parameter null or not set.

3

La forma explícita para comprobar si hay un ser variable definida sería:

[ -v mystr ] 
+1

No puedo encontrar esto en ninguna de las páginas man (para bash o prueba) pero funciona para mí ... ¿Alguna idea de qué versiones se agregaron? –

+1

Está documentado en [Expresiones condicionales] (http://www.gnu.org/software/bash/manual/bash.html#Bash-Conditional-Expressions), al que se hace referencia desde el operador 'test' en el extremo posterior del sección [Bourne Shell Builtins] (http://www.gnu.org/software/bash/manual/bash.html#Bourne-Shell-Builtins). No sé cuándo se agregó, pero no está en la versión de Apple de 'bash' (basado en' bash' 3.2.51), por lo que probablemente sea una característica 4.x. –

+1

Esto no funciona para mí con 'GNU bash, versión 4.1.2 (1) -release (x86_64-redhat-linux-gnu)'. El error es '-bash: [: -v: operador unario esperado'. Por un comentario [aquí] (http://stackoverflow.com/a/17538964/832230), se requiere como mínimo bash 4.2 para trabajar. –

5

https://stackoverflow.com/a/9824943/14731 contiene una mejor respuesta (uno que es más fácil de leer y trabaja con set -o nounset activado). 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 
1

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

He aquí un ejemplo de probar una variable para ver si existe:

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

(De section 6.3.2.)

Tenga en cuenta que el espacio en blanco después de la [ abierta y antes de la ] es no es opcional.


Consejos para los usuarios de Vim

que tenían un guión que tenía varias declaraciones de la siguiente manera:

export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part" 

Pero yo quería que difieren de los valores existentes. Así que les volvió a escribir a tener este aspecto:

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

yo era capaz de automatizar este en vim utilizando 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 mediante la selección de las líneas relevantes visualmente, a continuación, escribiendo :. La barra de comandos se rellena previamente con :'<,'>. Pega el comando de arriba y presiona enter.


comprobado en esta versión de Vim:

VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58) 
Compiled by [email protected] 

usuarios de Windows pueden desear diferentes finales de línea.

Cuestiones relacionadas