Comment tester si les éléments du tableau sont tous égaux en bash?

15

Le tableau suivant représente le nombre de disques sur chaque machine Linux

Chaque baie unique comprend le nombre de disques sur une machine Linux .

echo ${ARRAY_DISK_Quantity[*]}
4 4 4 4 2 4 4 4

quelle est la manière simple d'identifier que toutes les valeurs du tableau sont égales?

Bon état:

4 4 4 4 4 4 4 4

Mauvais état:

4 4 4 4 4 4 2 4

Mauvais état:

6 6 6 6 6 6 6 6 6 6 2 6 2
Yael
la source
Tant de réponses et pas de votes?
Jesse_b
Est-ce que cela testera uniquement des entiers ou devrait-il également tester des chaînes?
Jesse_b
J'attends juste la meilleure réponse ne vous inquiétez pas bientôt je voterai
yael
Je voulais dire tout le monde. Cette question mérite un vote positif de l'OMI.
Jesse_b
une fois que vous avez besoin de quelque chose d'au moins ce niveau de complexité, c'est le bon moment pour commencer à utiliser un vrai langage de programmation, jusqu'à ce qu'il soit trop tard…
Nom d'affichage

Réponses:

11

bash+ Solution GNU sort+ GNU grep:

if [ "${#array[@]}" -gt 0 ] && [ $(printf "%s\000" "${array[@]}" | 
       LC_ALL=C sort -z -u |
       grep -z -c .) -eq 1 ] ; then
  echo ok
else
  echo bad
fi

Explication en anglais: si le tri unique des éléments du tableau résulte en un seul élément, imprimez "ok". Sinon, imprimez "mauvais".

Le tableau est imprimé avec des octets NUL séparant chaque élément, canalisé en tri GNU (en s'appuyant sur les options -zaka --zero-terminatedet -uaka --unique), puis en grep(en utilisant les options -zaka --null-dataet -caka --count) pour compter les lignes de sortie.

Contrairement à ma version précédente, je ne peux pas l'utiliser wcici car cela nécessite des lignes d'entrée terminées par un retour à la ligne ... et l'utilisation de sedou trpour convertir les NUL en retour à la ligne après l' sortéchec du but d'utiliser les séparateurs NUL. grep -cfait un substitut raisonnable.


Voici la même chose réécrite en fonction:

function count_unique() {
  local LC_ALL=C

  if [ "$#" -eq 0 ] ; then 
    echo 0
  else
    echo "$(printf "%s\000" "$@" |
              sort --zero-terminated --unique |
              grep --null-data --count .)"
  fi
}



ARRAY_DISK_Quantity=(4 4 4 4 2 4 4 4)

if [ "$(count_unique "${ARRAY_DISK_Quantity[@]}")" -eq 1 ] ; then
  echo "ok"
else
  echo "bad"
fi
cas
la source
1
Notez que sort -ucela ne renvoie pas d'éléments uniques mais un de chaque ensemble d'éléments qui trient les mêmes. Par exemple, il dirait "ok" sur ARRAY_DISK_Quantity=(① ②)un système GNU où les locales décident généralement que ces 2 caractères sont triés de la même manière. Vous voudriez l' LC_ALL=C sort -uunicité d'octet à octet.
Stéphane Chazelas
juste une autre note, il échouera également au cas où aucun disque supplémentaire n'apparaîtrait de la CLI, vous devez donc également ajouter cette syntaxe
yael
[[`printf"% s \ n "" $ {ARRAY_DISK_Quantity [@]} "| wc -l `-eq` printf "% s \ n" "$ {ARRAY_DISK_Quantity [@]}" | grep -c "0" `]] && écho échoué
yael
@ StéphaneChazelas, le problème des paramètres régionaux mérite d'être traité, tout comme le problème IFS. Il est préférable de tester séparément une liste vide, IMO - il n'est pas nécessaire de vérifier les éléments non uniques dans un ensemble vide.
cas
Salut Cas, je préfère ta réponse précédente
Yael
8

Avec zsh:

if ((${#${(u)ARRAY_DISK_Quantity[@]}} == 1)); then
  echo OK
else
  echo not OK
fi

Où se (u)trouve un indicateur d'extension de paramètre pour développer des valeurs uniques . Nous obtenons donc un nombre de valeurs uniques dans le tableau.

Remplacez == 1par si <= 1vous voulez considérer qu'un tableau vide est OK.

Avec ksh93, vous pouvez trier le tableau et vérifier que le premier élément est le même que le dernier:

set -s -- "${ARRAY_DISK_Quantity[@]}"
if [ "$1" = "${@: -1}" ]; then
  echo OK
else
  echo not OK
fi

Avec ksh88 ou pdksh / mksh:

set -s -- "${ARRAY_DISK_Quantity[@]}"
if eval '[ "$1" = "${'"$#"'}" ]'; then
  echo OK
else
  echo not OK
fi

Avec bash, vous auriez probablement besoin d'une boucle:

unique_values() {
  typeset i
  for i do
    [ "$1" = "$i" ] || return 1
  done
  return 0
}
if unique_values "${ARRAY_DISK_Quantity[@]}"; then
  echo OK
else
  echo not OK
fi

(fonctionnerait avec tous les shells de type Bourne avec le support des tableaux (ksh, zsh, bash, yash)).

Notez qu'il renvoie OK pour un tableau vide. Ajoutez un [ "$#" -gt 0 ] || returnau début de la fonction si vous ne le souhaitez pas.

Stéphane Chazelas
la source
toutes ces réponses ne semblent pas soutenir bash?
yael
@yael, voir edit pour une solution bash. Mais pourquoi utiliseriez-vous bash?
Stéphane Chazelas
Dans Bash, la page d'aide de typesetindique Obsolete. See `help declare'.Y a-t-il une raison pour laquelle vous l'utilisez à la place de localou declare?
wjandrea
1
@wjandrea typesetest celui qui fonctionne dans les 4 coques. C'est également l' original de ksh au début des années 80 (bash a principalement copié ksh88 en ce qui concerne le paramètre et la déclaration du type de portée variable, mais a décidé de renommer typeset declareet de créer typesetun alias à déclarer).
Stéphane Chazelas
4

bash+ awksoltion:

function get_status() {
    arr=("$@")    # get the array passed as argument
    if awk 'v && $1!=v{ exit 1 }{ v=$1 }' <(printf "%d\n" "${arr[@]}"); then 
        echo "status: Ok"
    else 
        echo "status: Bad"
    fi
}

Cas de test n ° 1:

ARRAY_DISK_Quantity=(4 4 4 4 4 2 4 4)
get_status "${ARRAY_DISK_Quantity[@]}"
status: Bad

Cas de test n ° 2:

ARRAY_DISK_Quantity=(4 4 4 4 4 4 4 4)
get_status "${ARRAY_DISK_Quantity[@]}"
status: Ok
RomanPerekhrest
la source
4

J'ai une autre solution bash uniquement qui devrait également fonctionner avec des chaînes:

isarray.equal () {
    local placeholder="$1"
    local num=0
    while (( $# )); do
        if [[ "$1" != "$placeholder" ]]; then
            num=1
            echo 'Bad' && break
        fi
        shift
    done
    [[ "$num" -ne 1 ]] && echo 'Okay'
}

Manifestation:

[root@JBSTEST001 ~]# ARRAY_DISK_Quantity=(4 4 4 4 2 4 4 4)
[root@JBSTEST001 ~]# isarray.equal "${ARRAY_DISK_Quantity[@]}"
Bad
[root@JBSTEST001 ~]# ARRAY_DISK_Quantity=(4 4 4 4 4 4 4 4)
[root@JBSTEST001 ~]# isarray.equal "${ARRAY_DISK_Quantity[@]}"
Okay
[root@JBSTEST001 ~]# ARRAY_DISK_Quantity=(four four four four two four four four)
[root@JBSTEST001 ~]# isarray.equal "${ARRAY_DISK_Quantity[@]}"
Bad
[root@JBSTEST001 ~]# ARRAY_DISK_Quantity=(four four four four four four four four)
[root@JBSTEST001 ~]# isarray.equal "${ARRAY_DISK_Quantity[@]}"
Okay
Jesse_b
la source
Notez que les points ne sont pas valides dans les noms de fonction, bien que Bash soit assez permissif. Cela peut entraîner des problèmes comme lors de l'exportation de la fonction.
wjandrea
2

Avec bash et GNU grep:

if grep -qE '^([0-9]+)( \1)*$' <<< "${ARRAY_DISK_Quantity[@]}"; then 
  echo "okay"
else
  echo "not okay"
fi
Cyrus
la source
Oui, mais qu'en est-il (10 10 10 10)? Sinon, assez sympa.
Joe
@Joe: Bonne prise. J'ai mis à jour ma réponse.
Cyrus
1

Voici POSIX Awk:

awk 'BEGIN {while (++z < ARGC) if (ARGV[z] != ARGV[1]) exit 1}' "${ARRAY_DISK_Quantity[@]}"
Steven Penny
la source
0

bash seule solution ( en supposant aest ARRAY_DISK_Quantity)

ttt=${a[0]}
res=0
for i in "${a[@]}"
do 
    let res+=$(if [ "$ttt" -ne "$i" ]; then echo 1; else echo 0; fi);  
done
if [ "$res" -eq 0 ]
then 
    echo "ok"
else
    echo "bad"
fi
Bhavin Chirag
la source
Fonctionne, mais compte toutes les erreurs quand une seule suffit:if [ "$ttt" -ne "$i" ]; then res=1; break; fi;
Joe
0

Utilisez une boucle for pour comparer chaque élément du tableau au suivant. Terminez la boucle une itération de moins que la longueur du tableau pour éviter de comparer le dernier élément à rien à la fin.

for (( i=0; i<((${#array[@]}-1)); i++ )); do
    [ "${array[$i]}" != "${array[(($i+1))]}" ] && echo "Mismatch"
done
echo "Match"
Megengo
la source
Bienvenue sur U&L et merci pour votre contribution! Ce code affichera "Match" même en cas de non-concordance ... est-il destiné?
fra-san