Jak sprawdzić, czy wszystkie elementy tablicy są równe w bash?

15

Poniższa tablica reprezentuje liczbę dysków na każdym komputerze z systemem Linux

Każda pojedyncza tablica zawiera liczbę dysków na komputerze z systemem Linux .

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

jaki jest prosty sposób stwierdzenia, że ​​wszystkie wartości tablic są równe?

Dobry status:

4 4 4 4 4 4 4 4

Zły status:

4 4 4 4 4 4 2 4

Zły status:

6 6 6 6 6 6 6 6 6 6 2 6 2
Yael
źródło
Tyle odpowiedzi i brak głosów?
jesse_b
Czy będzie to tylko testowanie liczb całkowitych, czy też powinno to również testować ciągi znaków?
jesse_b
Po prostu czekam na najlepszą odpowiedź, nie martw się wkrótce Głosuję
yael
Miałem na myśli wszystkich innych. To pytanie zasługuje na głosowanie IMO.
jesse_b
gdy potrzebujesz czegoś o co najmniej takim stopniu złożoności, to dobry moment, aby zacząć używać prawdziwego języka programowania, aż będzie za późno…
Nazwa wyświetlana

Odpowiedzi:

11

bash+ GNU sort+ greprozwiązanie 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

Wyjaśnienie w języku angielskim: jeśli unikalne sortowanie elementów tablicy daje tylko jeden element, to wypisz „ok”. W przeciwnym razie wydrukuj „zły”.

Tablica jest drukowana z bajtami NUL oddzielającymi każdy element, przesyłana potokiem do sortowania GNU (zależnie od opcji -zaka --zero-terminatedi -uaka --unique), a następnie do grep(przy użyciu opcji -zaka --null-datai -caka --count) w celu zliczenia linii wyjściowych.

W przeciwieństwie do mojej poprzedniej wersji, nie mogę jej wctutaj użyć , ponieważ wymaga ona linii wejściowych zakończonych znakiem nowej linii ... i użycia sedlub trkonwersji NUL na znaki nowej linii po sortpokonaniu celu użycia separatorów NUL. grep -cstanowi rozsądny substytut.


Oto ta sama rzecz przepisana jako funkcja:

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
źródło
1
Zauważ, że sort -unie zwraca unikalnych elementów, ale jeden z każdego zestawu elementów, który sortuje to samo. Na przykład powiedziałby „ok” w ARRAY_DISK_Quantity=(① ②)systemach GNU, w których ustawienia regionalne zazwyczaj decydują o tym, że te 2 znaki sortują się tak samo. Chciałbyś LC_ALL=C sort -uwyjątkowości bajt po bajcie.
Stéphane Chazelas,
tylko kolejna uwaga, że ​​się nie powiedzie również w przypadku, gdy z interfejsu CLI nie pojawią się żadne dodatkowe dyski, więc należy również dodać tę składnię
yael
[[`printf"% s \ n "" $ {ARRAY_DISK_Quantity [@]} "| wc -l `-eq` printf "% s \ n" "$ {ARRAY_DISK_Quantity [@]}" | grep -c "0" ']] && echo fail
yael
@ StéphaneChazelas warto zająć się kwestią ustawień regionalnych, podobnie jak kwestią IFS. Testowanie pustej listy jest, IMO, najlepiej przeprowadzane osobno - nie ma potrzeby sprawdzania, czy w pustym zestawie nie są nie unikalne elementy.
cas
Cześć Cas Wolę twoją poprzednią odpowiedź
yael
8

Z zsh:

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

Gdzie (u)jest flaga rozwinięcia parametru, aby rozwinąć unikalne wartości. Otrzymujemy więc liczbę unikalnych wartości w tablicy.

Wymień == 1się <= 1to warto rozważyć pusta tablica jest OK.

Za pomocą ksh93możesz posortować tablicę i sprawdzić, czy pierwszy element jest taki sam jak ostatni:

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

Z ksh88 lub pdksh / mksh:

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

Z bashprawdopodobnie potrzebujesz pętli:

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

(działałby ze wszystkimi powłokami podobnymi do Bourne'a z obsługą tablicy (ksh, zsh, bash, yash)).

Zauważ, że zwraca OK dla pustej tablicy. Dodaj [ "$#" -gt 0 ] || returnna początku funkcji, jeśli nie chcesz tego.

Stéphane Chazelas
źródło
wszystkie te odpowiedzi nie wydają się wspierać bash?
yael
@yael, zobacz edycję rozwiązania bash. Ale dlaczego miałbyś skorzystać bash?
Stéphane Chazelas,
W Bash, strona pomoc dla typesetmówi Obsolete. See `help declare'.Czy istnieje powód używasz go zamiast locallub declare?
wjandrea
1
@wjandrea typesetto ten, który działa we wszystkich 4 powłokach. Jest to również oryginalna wersja ksh z początku lat 80. (bash kopiował głównie ksh88, jeśli chodzi o ustawienie i deklarowanie typu zmiennej zasięgu, ale postanowił zmienić nazwę typeset declarei utworzyć typesetalias do zadeklarowania).
Stéphane Chazelas,
4

bash+ awkrozwiązanie:

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
}

Przypadek testowy nr 1:

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

Przypadek testowy nr 2:

ARRAY_DISK_Quantity=(4 4 4 4 4 4 4 4)
get_status "${ARRAY_DISK_Quantity[@]}"
status: Ok
Roman Perekhrest
źródło
4

Mam inne rozwiązanie typu bash, które również powinno działać z ciągami:

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

Demonstracja:

[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
źródło
Zauważ, że kropki nie są poprawne w nazwach funkcji, chociaż Bash jest dość liberalny. Może to powodować problemy, takie jak eksportowanie funkcji.
wjandrea
2

Z bash i GNU grep:

if grep -qE '^([0-9]+)( \1)*$' <<< "${ARRAY_DISK_Quantity[@]}"; then 
  echo "okay"
else
  echo "not okay"
fi
Cyrus
źródło
Tak, ale co z (10 10 10 10)? W przeciwnym razie całkiem nieźle.
Joe
@Joe: Dobry chwyt. Zaktualizowałem swoją odpowiedź.
Cyrus
1

Oto POSIX Awk:

awk 'BEGIN {while (++z < ARGC) if (ARGV[z] != ARGV[1]) exit 1}' "${ARRAY_DISK_Quantity[@]}"
Steven Penny
źródło
0

rozwiązanie tylko bash (zakładając, że ajest 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
źródło
Działa, ale zlicza wszystkie błędy, gdy wystarczy jeden:if [ "$ttt" -ne "$i" ]; then res=1; break; fi;
Joe
0

Użyj pętli for, aby porównać każdy element tablicy z następnym. Zakończ pętlę o jedną iterację mniej niż długość tablicy, aby uniknąć porównywania ostatniego elementu z niczym na końcu.

for (( i=0; i<((${#array[@]}-1)); i++ )); do
    [ "${array[$i]}" != "${array[(($i+1))]}" ] && echo "Mismatch"
done
echo "Match"
megengo
źródło
Witamy w U&L i dziękujemy za Twój wkład! Ten kod wyświetli „Dopasuj”, nawet jeśli zostanie znalezione niedopasowanie… czy jest to zamierzone?
fra-san