Come verificare se gli elementi dell'array sono tutti uguali in bash?


15

La seguente matrice rappresentava il numero di dischi su ogni macchina linux

Ogni singolo array include il numero di dischi su una macchina linux .

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

qual è il modo semplice per identificare che tutti i valori dell'array sono uguali?

Buono stato:

4 4 4 4 4 4 4 4

Cattivo stato:

4 4 4 4 4 4 2 4

Cattivo stato:

6 6 6 6 6 6 6 6 6 6 2 6 2

Quante risposte e nessun voto?
Jesse_b,

Questo testerà solo numeri interi o dovrebbe anche testare le stringhe?
Jesse_b,

Sto solo aspettando la migliore risposta, non ti preoccupare, presto voterò
yael,

Intendevo tutti gli altri. Questa domanda merita di essere votata IMO.
Jesse_b,

una volta che hai bisogno di qualcosa di almeno questo livello di complessità, è un buon momento per iniziare a usare un vero linguaggio di programmazione, fino a quando non è troppo tardi ...
Visualizza nome

Risposte:


11

bash+ GNU sort+ grepsoluzione GNU :

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

Spiegazione inglese: se l'ordinamento univoco degli elementi dell'array risulta in un solo elemento, quindi stampare "ok". Altrimenti stampa "cattivo".

L'array viene stampato con byte NUL che separano ciascun elemento, convogliato nell'ordinamento GNU (basandosi sulle opzioni -zaka --zero-terminatede -uaka --unique) e quindi in grep(utilizzando le opzioni -zaka --null-datae -caka --count) per contare le linee di output.

A differenza della mia versione precedente, non posso usare wcqui perché richiede linee di input terminate con una nuova riga ... e l'utilizzo sedo la trconversione di NUL in nuove righe dopo che sortavrebbe vanificato l'obiettivo di utilizzare i separatori NUL. grep -cfa un sostituto ragionevole.


Ecco la stessa cosa riscritta come funzione:

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

1
Si noti che sort -unon restituisce elementi univoci ma uno di ogni set di elementi che ordinano lo stesso. Ad esempio, direbbe "ok" su ARRAY_DISK_Quantity=(① ②)un sistema GNU in cui le impostazioni locali in genere decidono che i 2 caratteri ordinino lo stesso. Vorresti LC_ALL=C sort -uper l'unicità byte-to-byte.
Stéphane Chazelas il

solo un'altra nota fallirà anche nel caso in cui non ci siano altri dischi dalla CLI, quindi è necessario aggiungere anche questa sintassi
yael

[[`` printf "% s \ n" "$ {ARRAY_DISK_Quantity [@]}" | wc -l `-eq` printf "% s \ n" "$ {ARRAY_DISK_Quantity [@]}" | grep -c "0" `]] && echo fail
yael

@ StéphaneChazelas vale la pena di risolvere il problema locale, così come il problema IFS. Il test per un elenco vuoto è, IMO, da eseguire separatamente: non è necessario verificare la presenza di elementi non univoci in un set vuoto.
Cas

Ciao Cas, preferisco la tua risposta precedente
yael,

8

Con zsh:

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

Dove si (u)trova un flag di espansione dei parametri per espandere valori univoci . Quindi stiamo ottenendo un conteggio dei valori univoci nella matrice.

Sostituisci == 1con <= 1se vuoi considerare che un array vuoto è OK.

Con ksh93, è possibile ordinare l'array e verificare che il primo elemento sia uguale all'ultimo:

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

Con ksh88 o pdksh / mksh:

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

Con bash, probabilmente avresti bisogno di un ciclo:

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

(funzionerebbe con tutte le shell tipo Bourne con supporto array (ksh, zsh, bash, yash)).

Si noti che restituisce OK per un array vuoto. Aggiungere un[ "$#" -gt 0 ] || return a all'inizio della funzione se non lo desideri.


tutte queste risposte non sembrano supportare bash?
yael

@yael, vedi modifica per una soluzione bash. Ma perché dovresti usare bash?
Stéphane Chazelas,

In Bash, la pagina di aiuto per typesetdice Obsolete. See `help declare'.C'è un motivo per cui lo stai usando al posto di localo declare?
wjandrea,

1
@wjandrea typesetè quello che funziona in tutte e 4 le shell. È anche quello originale di ksh nei primi anni '80 (bash per lo più copiato ksh88 quando si tratta di impostazione e dichiarazione del tipo di scoping variabile ma ha deciso di rinominare typeset declaree creare typesetun alias da dichiarare).
Stéphane Chazelas il

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
}

Caso di test n. 1:

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

Caso di test n. 2:

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

4

Ho un'altra soluzione solo bash che dovrebbe funzionare anche con le stringhe:

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'
}

Dimostrazione:

[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

Nota che i punti non sono validi nei nomi delle funzioni, sebbene Bash sia abbastanza permissivo. Ciò può causare problemi come nell'esportazione della funzione.
wjandrea,


2

Con bash e GNU grep:

if grep -qE '^([0-9]+)( \1)*$' <<< "${ARRAY_DISK_Quantity[@]}"; then 
  echo "okay"
else
  echo "not okay"
fi

Sì, ma che dire di (10 10 10 10)? Altrimenti, abbastanza carino.
Joe,

@Joe: buona cattura. Ho aggiornato la mia risposta.
Ciro il

1

Ecco POSIX Awk:

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

0

bash only solution (ammesso che asia 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

Funziona, ma conta tutti gli errori quando ne basta uno solo:if [ "$ttt" -ne "$i" ]; then res=1; break; fi;
Joe

0

Utilizzare un ciclo for per confrontare ciascun elemento dell'array con il successivo. Terminare il ciclo con un'iterazione inferiore alla lunghezza dell'array per evitare di confrontare l'ultimo elemento con nulla alla fine.

for (( i=0; i<((${#array[@]}-1)); i++ )); do
    [ "${array[$i]}" != "${array[(($i+1))]}" ] && echo "Mismatch"
done
echo "Match"

Benvenuto in U&L e grazie per il tuo contributo! Questo codice stamperà "Abbina" anche se viene rilevata una mancata corrispondenza ... è previsto?
fra-san,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.