Pregunta Compruebe si la matriz está vacía en Bash


Tengo una matriz que se llena con diferentes mensajes de error mientras se ejecuta mi script.

Necesito una forma de verificar si está vacío o no al final del script y tomar una acción específica si lo está.

Ya he intentado tratarlo como un VAR normal y usar -z para comprobarlo, pero eso no parece funcionar. ¿Hay alguna forma de verificar si una matriz está vacía o no en Bash?

Gracias


82
2018-02-11 03:59


origen




Respuestas:


Suponiendo que tu matriz es $errors, solo verifica si el conteo de elementos es cero.

if [ ${#errors[@]} -eq 0 ]; then
    echo "No errors, hooray"
else
    echo "Oops, something went wrong..."
fi

113
2018-02-11 04:10



Tenga en cuenta que = es un operador de cadena. Resulta que funciona bien en este caso, pero usaría el operador aritmético adecuado -eq en su lugar (por si acaso quisiera cambiar a -ge o -lt, etc.). - musiphil
No trabaja con set -u: "variable no vinculada" - si la matriz está vacía. - Igor
@ Igor: funciona para mí en Bash 4.4. set -u;  foo=();  [ ${#foo[@]} -eq 0 ] && echo empty. Si yo unset foo, luego se imprime foo: unbound variable, pero eso es diferente: la variable de matriz no existe en absoluto, en lugar de existir y estar vacía. - Peter Cordes
También probado en Bash 3.2 (OSX) cuando se usa set -u - Mientras declare su variable primero, esto funciona perfectamente. - zeroimpl


También puede considerar la matriz como una variable simple. De esa manera, simplemente usando

if [ -z "$array" ]; then
    echo "Array empty"
else
    echo "Array non empty"
fi

o usando el otro lado

if [ -n "$array" ]; then
    echo "Array non empty"
else
    echo "Array empty"
fi

El problema con esa solución es que si una matriz se declara así: array=('' foo). Estas verificaciones informarán que la matriz está vacía, mientras que claramente no lo está. (gracias @musiphil!)

Utilizando [ -z "$array[@]" ] Claramente no es una solución tampoco. Sin especificar corchetes intenta interpretar. $array como una cuerda ([@] es en ese caso una cadena literal simple) y, por lo tanto, siempre se informa como falso: "es la cadena literal [@] ¿Vacío? "Claramente no.


6
2018-06-23 10:29



[ -z "$array" ] o [ -n "$array" ] no funciona Tratar array=('' foo); [ -z "$array" ] && echo empty, y se imprimirá empty aunque array claramente no está vacío. - musiphil
[[ -n "${array[*]}" ]] interpola toda la matriz como una cadena, que verificas para una longitud distinta de cero. Si consideras array=("" "") para estar vacío, en lugar de tener dos elementos vacíos, esto podría ser útil. - Peter Cordes


Generalmente uso expansión aritmética en este caso:

if (( ${#a[@]} )); then
    echo not empty
fi

3
2017-08-02 06:04



¡Bonito y limpio! Me gusta. También tengo en cuenta que si el primer elemento de la matriz es siempre no vacío, (( ${#a} )) (longitud del primer elemento) funcionará también. Sin embargo, eso fallará a=(''), mientras que (( ${#a[@]} )) dado en la respuesta tendrá éxito. - cxw


Lo comprobé con bash-4.4.0:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]} ]]; then
        echo not empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

y bash-4.1.5:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]:+${array[@]}} ]]; then
        echo non-empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

En este último caso necesitas el siguiente constructo:

${array[@]:+${array[@]}}

para que no falle en una matriz vacía o no configurada. Eso si lo haces set -eu como yo suelo hacer. Esto proporciona una comprobación de errores más estricta. Desde los docs:

-mi

Salga de inmediato si una canalización (ver Canalizaciones), que puede consistir en un solo comando simple (consulte Comandos simples), una lista (consulte Listas) o un comando compuesto (consulte Comandos compuestos) devuelve un estado distinto de cero. El shell no se cierra si el comando que falla es parte de la lista de comandos inmediatamente después de un tiempo o hasta que la palabra clave, parte de la prueba en una sentencia if, parte de cualquier comando ejecutado en un && o || lista, excepto el comando que sigue a && o final, cualquier comando en una canalización pero el último, o si el estado de retorno del comando se invierte con!. Si un comando compuesto que no sea un subshell devuelve un estado distinto de cero porque un comando falló mientras se ignoraba -e, el shell no se cierra. Una captura en ERR, si está establecida, se ejecuta antes de que el shell salga.

Esta opción se aplica al entorno de shell y a cada entorno de subshell por separado (consulte Entorno de ejecución de comandos) y puede hacer que las subshells se cierren antes de ejecutar todos los comandos en el subshell.

Si un comando compuesto o una función de shell se ejecuta en un contexto donde se ignora -e, ninguno de los comandos ejecutados dentro del comando compuesto o el cuerpo de la función se verán afectados por la configuración -e, incluso si se establece -e y un comando devuelve un estado de fallo. Si un comando compuesto o una función de shell establece -e mientras se ejecuta en un contexto donde -e se ignora, esa configuración no tendrá ningún efecto hasta que se complete el comando compuesto o el comando que contiene la llamada a la función.

-u

Trate las variables y parámetros distintos de los parámetros especiales '@' o '*' como un error al realizar la expansión de parámetros. Se escribirá un mensaje de error en el error estándar y se cerrará un shell no interactivo.

Si no lo necesitas, siéntete libre de omitir :+${array[@]} parte.

También tenga en cuenta, que es esencial utilizar [[ operador aquí, con [ usted obtiene:

$ cat 1.sh
#!/usr/bin/env bash
set -eu
array=(a b c d)
if [ "${array[@]}" ]; then
    echo non-empty
else
    echo empty
fi

$ ./1.sh
_/1.sh: line 4: [: too many arguments
empty

2
2017-12-04 14:58



Con -u deberías usar ${array[@]+"${array[@]}"} cf stackoverflow.com/a/34361807/1237617 - Jakub Bochenski
@JakubBochenski ¿De qué versión de bash estás hablando? gist.github.com/x-yuri/d933972a2f1c42a49fc7999b8d5c50b9 - x-yuri
El problema en el ejemplo de corchetes individuales es el @seguramente Usted podría usar * expansión de matriz como [ "${array[*]}" ], ¿Podrías no? Todavía, [[ También funciona bien. El comportamiento de ambos para una matriz con múltiples cadenas vacías es un poco sorprendente. Ambos [ ${#array[*]} ] y [[ "${array[@]}" ]] son falsos para array=() y array=('') pero cierto para array=('' '') (dos o mas cuerdas vacias). Si quisieras que una o más cadenas vacías dieran verdadero, podrías usar [ ${#array[@]} -gt 0 ]. Si los quisieras todos falsos, tal vez podrías // ellos afuera. - eisd
@eisd podría usar [ "${array[*]}" ], pero si tuviera que encontrar esa expresión, me sería más difícil entender lo que hace. Ya que [...] Opera en términos de cadenas sobre el resultado de la interpolación. Opuesto a [[...]], que puede ser consciente de lo que fue interpolado. Es decir, puede saber que se le pasó una matriz. [[ ${array[@]} ]] me lee como "verifica si la matriz no está vacía", mientras que [ "${array[*]}" ] como "compruebe si el resultado de la interpolación de todos los elementos de la matriz es una cadena no vacía". - x-yuri
... En cuanto al comportamiento con dos cuerdas vacías, no me sorprende en absoluto. Lo que sorprende es el comportamiento con una cadena vacía. Pero podría decirse que es razonable. Respecto a [ ${#array[*]} ]Probablemente quisiste decir [ "${array[*]}" ], ya que lo primero es cierto para cualquier número de elementos. Porque el número de elementos es siempre una cadena no vacía. Respecto a este último con dos elementos, la expresión entre paréntesis se expande a ' ' que es una cadena no vacía. Como para [[ ${array[@]} ]], simplemente piensan (y con razón) que cualquier matriz de dos elementos no está vacía. - x-yuri


En mi caso, la segunda respuesta No era suficiente porque podría haber espacios en blanco. Vine con:

if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
  echo "No options"
else
  echo "Options found"
fi

0
2018-02-17 19:54



echo | wc Parece innecesariamente ineficiente en comparación con el uso de shell incorporado. - Peter Cordes
No estoy seguro si entiendo @PeterCordes, ¿puedo modificar las segundas respuestas? [ ${#errors[@]} -eq 0 ]; en una forma de solucionar el problema de los espacios en blanco? También preferiría el empotrado. - Micha
¿Cómo exactamente causa un espacio en blanco un problema? $# se expande a un número, y funciona bien incluso después opts+=(""). p.ej. unset opts;  opts+=("");opts+=(" "); echo "${#opts[@]}" y me pongo 2. ¿Puedes mostrar un ejemplo de algo que no funciona? - Peter Cordes
Hace mucho tiempo. IIRC la fuente de origen siempre se imprime al menos "". Por lo tanto, para opts = "" u opts = ("") necesitaba 0, no 1, ignorando la nueva línea vacía o la cadena vacía. - Micha
Ok, entonces necesitas tratar opts=("") lo mismo que opts=()? Eso no es una matriz vacía, pero puede verificar la matriz vacía o el primer elemento vacío con opts=("");  [[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty. Tenga en cuenta que su respuesta actual dice "sin opciones" para opts=("" "-foo"), lo cual es totalmente falso, y esto reproduce ese comportamiento. Tú podrías [[ -z "${opts[*]}" ]] Supongo que, para interpolar todos los elementos de la matriz en una cadena plana, que -z comprueba la longitud no cero.  Si comprobar el primer elemento es suficiente, -z "$opts" trabajos. - Peter Cordes


Prefiero usar dobles corchetes:

if [[ !${array[@]} ]]
then
    echo "Array is empty"
else
    echo "Array is not empty"
fi

Soportes dobles: https://stackoverflow.com/questions/669452/is-preferable-over-in-bash


0
2017-11-04 06:40