¿Cómo puedo probar si una variable está vacía o contiene solo espacios?


240

La siguiente sintaxis bash verifica si paramno está vacía:

 [[ !  -z  $param  ]]

Por ejemplo:

param=""
[[ !  -z  $param  ]] && echo "I am not zero"

No hay salida y está bien.

Pero cuando paramestá vacío excepto por uno (o más) caracteres de espacio, entonces el caso es diferente:

param=" " # one space
[[ !  -z  $param  ]] && echo "I am not zero"

Se emite "No soy cero".

¿Cómo puedo cambiar la prueba para considerar variables que contienen solo caracteres de espacio como vacías?


44
De man test: -z STRING - the length of STRING is zero. Si desea eliminar todos los espacios $param, use${param// /}
dchirikov

66
WOW, ¿ no hay una trim()función simple incorporada en cualquier * nix? Tantos
trucos

66
@ADTC $ (sed 's / ^ \ s + | \ s + $ // g' <<< $ string) me parece bastante simple. ¿Por qué reinventar la rueda?
jbowman

3
Porque podría ser más brillante
Inversus

1
Solo notando que [[ ! -z $param ]]es equivalente a test ! -z $param.
Noah Sussman

Respuestas:


292

Primero, tenga en cuenta que la -zprueba es explícitamente para:

la longitud de la cuerda es cero

Es decir, una cadena que contiene solo espacios no debería ser verdadera debajo -z, porque tiene una longitud distinta de cero.

Lo que desea es eliminar los espacios de la variable utilizando la expansión del parámetro de reemplazo de patrón :

[[ -z "${param// }" ]]

Esto expande la paramvariable y reemplaza todas las coincidencias del patrón (un solo espacio) con nada, por lo que una cadena que solo tiene espacios se expandirá a una cadena vacía.


Lo esencial de cómo funciona eso es que ${var/pattern/string}reemplaza la primera combinación más larga de patterncon string. Cuando patterncomienza con /(como arriba), reemplaza todos los partidos. Como el reemplazo está vacío, podemos omitir el final /y el stringvalor:

$ {parámetro / patrón / cadena}

El patrón se expande para producir un patrón al igual que en la expansión de nombre de archivo. El parámetro se expande y la coincidencia más larga del patrón contra su valor se reemplaza con una cadena . Si el patrón comienza con '/', todas las coincidencias del patrón se reemplazan con una cadena . Normalmente solo se reemplaza el primer partido. ... Si la cadena es nula, se eliminan las coincidencias de patrón y se puede omitir el / patrón siguiente .

Después de todo eso, terminamos con ${param// }eliminar todos los espacios.

Tenga en cuenta que aunque está presente en ksh(donde se originó), zshy bash, esa sintaxis no es POSIX y no debe usarse en shscripts.


1
uso seddijeron ... solo echola variable que dijeron ...
mikezter

1
Hmm Si es ${var/pattern/string}así, ¿no debería ser [[ -z ${param/ /} ]]con el espacio entre las barras el patrón y nada más que la cadena?
Jesse Chisholm

@JesseChisholm "Debido a que el reemplazo está vacío, podemos omitir el valor final / y el valor de cadena"; "Si la cadena es nula, se eliminan las coincidencias de patrón y se puede omitir el / patrón siguiente ".
Michael Homer

66
@MichaelHomer La respuesta [[ -z "${param// }" ]]seguramente parece que patternestá vacía y replacement stringes un espacio único. AH! Ahora lo veo, if pattern begins with a slashme perdí esa parte. entonces el patrón es / y la cadena se deja y, por lo tanto, está vacía. Gracias.
Jesse Chisholm

nota bash: si se ejecuta en set -o nounset (set -u), y param no está establecido (en lugar de nulo o lleno de espacio), esto generará un error de variable independiente. (set + u; .....) eximiría esta prueba.
Brian Chrisman

31

La manera fácil de verificar que una cadena solo contenga caracteres en un conjunto autorizado es probar la presencia de caracteres no autorizados. Por lo tanto, en lugar de probar si la cadena solo contiene espacios, pruebe si la cadena contiene algún carácter que no sea espacio. En bash, ksh o zsh:

if [[ $param = *[!\ ]* ]]; then
  echo "\$param contains characters other than space"
else
  echo "\$param consists of spaces only"
fi

“Consiste solo en espacios” incluye el caso de una variable vacía (o no establecida).

Es posible que desee probar cualquier carácter de espacio en blanco. Utilícelo [[ $param = *[^[:space:]]* ]]para usar la configuración regional o cualquier lista explícita de caracteres de espacios en blanco que desee probar, por ejemplo, [[ $param = *[$' \t\n']* ]]para probar el espacio, la pestaña o la nueva línea.

Hacer coincidir una cadena con un patrón con el =interior [[ … ]]es una extensión ksh (también presente en bash y zsh). En cualquier estilo Bourne / POSIX, puede usar la caseconstrucción para hacer coincidir una cadena con un patrón. Tenga en cuenta que los patrones de shell estándar se utilizan !para negar un conjunto de caracteres, en lugar de ^como en la mayoría de las sintaxis de expresiones regulares.

case "$param" in
  *[!\ ]*) echo "\$param contains characters other than space";;
  *) echo "\$param consists of spaces only";;
esac

Para probar los caracteres de espacios en blanco, la $'…'sintaxis es específica de ksh / bash / zsh; puede insertar estos caracteres en su script literalmente (tenga en cuenta que una nueva línea tendrá que estar entre comillas, ya que la barra invertida + nueva línea se expande a nada), o generarlos, por ejemplo

whitespace=$(printf '\n\t ')
case "$param" in
  *[!$whitespace]*) echo "\$param contains non-whitespace characters";;
  *) echo "\$param consists of whitespace only";;
esac

Esto es casi excelente, pero, por el momento, no responde a la pregunta que se le hizo. Actualmente puede decirle la diferencia entre una variable de shell no establecida o vacía y una que contiene solo espacios. Si acepta mi sugerencia, agregará el formulario de expansión de parámetros que aún no está cubierto por ninguna otra respuesta que pruebe explícitamente que se establezca una variable de shell, es decir ${var?not set}, pasar esa prueba y sobrevivir garantizaría que una variable coincidente con la suya tenga *) caseun efecto definitivo respuesta, por ejemplo.
mikeserv

Corrección - esto, de hecho, podría responder a la pregunta planteada inicialmente, pero desde entonces ha sido editado de tal manera que fundamentalmente cambia el significado de la pregunta y por lo tanto invalida su respuesta.
mikeserv

Es necesario [[ $param = *[!\ ]* ]]en mksh.
Stéphane Chazelas

20

POSIXY:

case $var in
  (*[![:blank:]]*) echo '$var contains non blank';;
  (*) echo '$var contains only blanks or is empty or unset'
esac

Para diferenciar entre en blanco , no esté en blanco , vacío , unset :

case ${var+x$var} in
  (x) echo empty;;
  ("") echo unset;;
  (x*[![:blank:]]*) echo non-blank;;
  (*) echo blank
esac

[:blank:]es para los caracteres de espaciado horizontal (espacio y tabulación en ASCII, pero probablemente haya algunos más en su ubicación; algunos sistemas incluirán el espacio que no se rompe (donde esté disponible), otros no). Si también desea caracteres de espaciado vertical (como nueva línea o avance de formulario), reemplácelos [:blank:]por [:space:].


POSIXly es ideal porque funcionará con el guión (posiblemente mejor).
Ken Sharp

zshparece tener problemas con [![:blank:]], elevar :bes modificador no válido. En shy kshemular, se [
genera un

@cuonglm, ¿qué intentaste? var=foo zsh -c 'case ${var+x$var} in (x*[![:blank:]]*) echo x; esac'esta bien para mi. El evento no encontrado solo sería para shells interactivos.
Stéphane Chazelas

@ StéphaneChazelas: Ah, claro, lo intenté con shells interactivos. El :bmodificador inválido es un poco extraño.
Cuonglm

1
@FranklinYu, en OS / X shestá bashconstruido con --enable-xpg-echo-defaulty --enable-strict-posix-defaultpara ser compatible con Unix (lo que implica echo '\t'generar una pestaña).
Stéphane Chazelas

4

La única razón restante para escribir un script de shell, en lugar de un script en un buen lenguaje de script, es si la portabilidad extrema es una preocupación primordial. El legado /bin/shes lo único que puede estar seguro de que tiene, pero Perl, por ejemplo, es más probable que esté disponible multiplataforma que Bash. Por lo tanto, nunca escriba scripts de shell que utilicen funciones que no sean realmente universales , y tenga en cuenta que varios proveedores de Unix propietarios congelaron su entorno de shell antes de POSIX.1-2001 .

Hay una forma portátil de hacer esta prueba, pero debe usar tr:

[ "x`printf '%s' "$var" | tr -d "$IFS"`" = x ]

(El valor predeterminado de $IFS, convenientemente, es un espacio, una pestaña y una nueva línea).

(La printfconstrucción en sí es muy portátil, pero confiar en ella es mucho menos molesta que descubrir qué variante echotiene).


1
Eso es un poco complicado. La forma portátil habitual de probar si una cadena contiene ciertos caracteres es concase .
Gilles

@Gilles No creo que sea posible escribir un caseglobo para detectar una cadena que está vacía o que contiene solo caracteres de espacio en blanco. (Sería fácil con una caseexpresión regular , pero no hace expresiones regulares. La construcción en su respuesta no funcionará si le importan las nuevas líneas.)
zwol

1
Di espacios como ejemplo en mi respuesta porque eso es lo que pedía la pregunta. Es igualmente fácil para la prueba de los espacios en blanco: case $param in *[![:space:]]*) …. Si desea probar los IFScaracteres, puede usar el patrón *[$IFS]*.
Gilles

1
@mikeserv En un muy serio guión defensiva, se establece explícitamente a IFS <space><tab><newline>al principio y luego nunca toca de nuevo. Pensé que sería una distracción.
zwol

1
Con respecto a las variables en los patrones, creo que funciona en todas las versiones de Bourne y todos los shells POSIX; requeriría un código adicional para detectar patrones de casos y desactivar la expansión variable y no se me ocurre una razón para hacerlo. Tengo un par de instancias en mi .profileque ha sido ejecutado por muchos proyectiles Bourne. Por supuesto [$IFS], no hará lo que se pretendía si IFStiene ciertos valores no predeterminados (vacíos (o sin establecer), que contienen ], comienzan con !o ^).
Gilles

4
if [[ -n "${variable_name/[ ]*\n/}" ]]
then
    #execute if the the variable is not empty and contains non space characters
else
    #execute if the variable is empty or contains only spaces
fi

esto elimina cualquier espacio en blanco, no solo un espacio, ¿verdad? gracias
Alexander Mills

0

Para probar si la variable está vacía o contiene espacios, también puede usar este código:

${name:?variable is empty}

2
Creo que su respuesta es rechazada porque "o contener espacios" no es cierto.
Bernhard

ten cuidado, eso mata el caparazón actual. Es mejor ponerlo en un (: $ {subshell?}). Además, eso escribirá en stderr, probablemente desee redirigir. Y lo más importante es que evalúa el valor real de la variable si no está desarmado o nulo. Si hay un comando allí, simplemente lo ejecutó. Es mejor ejecutar esa prueba :.
mikeserv

Cómo matará a Shell. y también puede probar esto, primero defina, name=aaaaluego ejecute este comando echo ${name:?variable is empty}, imprimirá el valor del nombre de la variable y ahora ejecute este comando echo ${name1:?variable is empty}, imprimirá -sh: nombre1: la variable está vacía
pratik

Sí, echo ${name:?variable is empty}evaluará el $namevalor no nulo o matará al shell. Entonces, por la misma razón que usted no hace, prompt > $randomvartampoco debería hacerlo ${var?}: si hay un comando allí, probablemente se ejecutará, y no sabe qué es. Un shell interactivo no tiene que salir, por eso el tuyo no. Aún así, si desea ver algunas pruebas, hay muchas de ellas aquí. . Este no es tan largo ...
mikeserv

0

Para probar si una variable no está definida, está vacía (tiene un valor nulo) o contiene solo espacios:

 [ -z "${param#"${param%%[! ]*}"}" ] && echo "empty"

Explicación:

  • La expansión de ${param%%[! ]*}elimina desde el primer no espacio hasta el final.
  • Eso deja solo los espacios principales.
  • La próxima expansión elimina los espacios iniciales de la variable.
  • Si el resultado está vacío, entonces la variable estaba vacía para empezar.
  • O, si la variable no estaba vacía, la eliminación de los espacios iniciales la hacía vacía.
  • Si el resultado está vacío -z, entonces echo empty.

Lo anterior es compatible con POSIX y se ejecuta en cualquier descendiente Bourne.

Si desea extender eso a las pestañas también, incluya una pestaña explícita:

 [ -z "${param#"${param%%[!     ]*}"}" ] && echo "empty"

o use una variable con los caracteres que necesita eliminar:

 var=$' \t'   # in ksh, bash, zsh
 [ -z "${param#"${param%%[!$var]*}"}" ] && echo "empty"

o puede usar alguna "expresión de clase de caracteres" POSIX (en blanco significa espacio y tabulación):

 [ -z "${param#"${param%%[![:blank:]]*}"}" ] && echo "empty"

Pero eso fallará en mksh, lksh y posh.


-1

Prueba esto:

$ [[ -z \`echo $n\` ]] && echo zero

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