web-dev-qa-db-fra.com

Vérifiez si le tableau est vide dans Bash

J'ai un tableau qui se remplit de différents messages d'erreur lors de l'exécution de mon script.

J'ai besoin d'un moyen de vérifier s'il est vide ou non à la fin du script et de prendre une action spécifique si c'est le cas.

J'ai déjà essayé de le traiter comme un VAR normal et d'utiliser -z pour le vérifier, mais cela ne semble pas fonctionner. Existe-t-il un moyen de vérifier si un tableau est vide ou non dans Bash?

123
Marcos Sander

Supposons que votre tableau soit $errors, vérifiez simplement si le nombre d'éléments est nul.

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

J'utilise généralement l'expansion arithmétique dans ce cas:

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

Vous pouvez également considérer le tableau comme une simple variable. De cette façon, en utilisant simplement

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

ou en utilisant l'autre côté

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

Le problème avec cette solution est que si un tableau est déclaré comme ceci: array=('' foo). Ces vérifications rapporteront le tableau comme vide, alors qu'il ne l'est clairement pas. (merci @musiphil!)

L'utilisation de [ -z "$array[@]" ] N'est clairement pas une solution non plus. Ne pas spécifier de crochets essaie d'interpréter $array Comme une chaîne ([@] Est dans ce cas une chaîne littérale simple) et est donc toujours signalé comme faux: "est la chaîne littérale [@] vide?" Pas du tout.

9
wget

Je l'ai vérifié avec 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

et 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

Dans ce dernier cas, vous avez besoin de la construction suivante:

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

pour qu'il n'échoue pas sur un tableau vide ou non défini. C'est si vous faites set -eu comme d'habitude. Cela permet une vérification d'erreur plus stricte. De les docs :

-e

Quittez immédiatement si un pipeline (voir Pipelines), qui peut consister en une seule commande simple (voir Commandes simples), une liste (voir Listes) ou une commande composée (voir Commandes composées) renvoie un état différent de zéro. Le shell ne se ferme pas si la commande qui échoue fait partie de la liste de commandes immédiatement après un mot clé while ou until, une partie du test dans une instruction if, une partie de toute commande exécutée dans un && ou || liste à l'exception de la commande suivant le && ou || final, toute commande dans un pipeline mais la dernière, ou si le statut de retour de la commande est inversé avec!. Si une commande composée autre qu'un sous-shell renvoie un état différent de zéro car une commande a échoué alors que -e était ignoré, le shell ne se ferme pas. Un trap sur ERR, s'il est défini, est exécuté avant la fermeture du shell.

Cette option s'applique à l'environnement Shell et à chaque environnement de sous-shell séparément (voir Environnement d'exécution de commandes) et peut entraîner la fermeture des sous-shells avant d'exécuter toutes les commandes du sous-shell.

Si une commande composée ou une fonction Shell s'exécute dans un contexte où -e est ignoré, aucune des commandes exécutées dans la commande composée ou le corps de la fonction ne sera affectée par le paramètre -e, même si -e est défini et qu'une commande renvoie un état d'échec. Si une commande composée ou une fonction Shell définit -e lors de l'exécution dans un contexte où -e est ignoré, ce paramètre n'aura aucun effet tant que la commande composée ou la commande contenant l'appel de fonction ne sera pas terminée.

-u

Traitez les variables et paramètres non définis autres que les paramètres spéciaux ‘@’ ou ‘*’ comme une erreur lors de l’expansion des paramètres. Un message d'erreur sera écrit dans l'erreur standard et un shell non interactif se fermera.

Si vous n'en avez pas besoin, n'hésitez pas à omettre :+${array[@]} partie.

Notez également qu'il est essentiel d'utiliser [[ opérateur ici, avec [ vous obtenez:

$ 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
3
x-yuri

Si vous voulez détecter un tableau avec des éléments vides , comme arr=("" "") comme vide, comme arr=()

Vous pouvez coller tous les éléments ensemble et vérifier si le résultat est de longueur nulle. (Construire une copie aplatie du contenu du tableau n'est pas idéal pour les performances si le tableau peut être très grand. Mais j'espère que vous n'utilisez pas bash pour des programmes comme ça ...)

Mais "${arr[*]}" Se développe avec des éléments séparés par le premier caractère de IFS. Vous devez donc enregistrer/restaurer IFS et faire IFS='' Pour que cela fonctionne, ou bien vérifier que la longueur de chaîne == # des éléments du tableau - 1. (Un tableau d'éléments n a n-1 Séparateurs). Pour gérer ce problème, il est plus facile de remplir la concaténation de 1

arr=("" "")

## Assuming default non-empty IFS
## TODO: also check for ${#arr[@]} -eq 0
concat="${arr[*]} "      # n-1 separators + 1 space + array elements
[[ "${#concat}" -ne "${#arr[@]}" ]]  && echo not empty array || echo empty array

cas de test avec set -x

### a non-empty element
$ arr=("" "x")
  + arr=("" "x")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat=' x '
  + [[ 3 -ne 2 ]]
  + echo not empty array
not empty array

### 2 empty elements
$ arr=("" "")
  + arr=("" "")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat='  '
  + [[ 2 -ne 2 ]]
  + echo empty array
empty array

Malheureusement, cela échoue pour arr=(): [[ 1 -ne 0 ]]. Vous devez donc d'abord vérifier séparément les tableaux vides.


Ou avec IFS=''. Vous voudrez probablement enregistrer/restaurer IFS au lieu d'utiliser un sous-shell, car vous ne pouvez pas obtenir facilement un résultat d'un sous-shell.

# inside a () subshell so we don't modify our own IFS
(IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)

exemple:

$ arr=("" "")
$ (IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)
   + IFS=
   + [[ -n '' ]]
   + echo empty array
empty array

fonctionne avec arr=() - c'est toujours juste la chaîne vide.

2
Peter Cordes

Je préfère utiliser des crochets doubles:

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

Supports doubles: https://stackoverflow.com/questions/669452/is-preferable-over-in-bash

1
Nick Tsai

Dans mon cas, la deuxième réponse n'était pas suffisante car il pouvait y avoir des espaces blancs. Je suis venu avec:

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