Wie teste ich, ob alle Array-Elemente in bash gleich sind?


15

Das folgende Array repräsentiert die Anzahl der Festplatten auf jedem Linux-Computer

Jedes einzelne Array enthält die Anzahl der Festplatten auf einem Linux-Computer .

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

Wie lässt sich auf einfache Weise feststellen, dass alle Array-Werte gleich sind?

Guter Zustand:

4 4 4 4 4 4 4 4

Schlechter Status:

4 4 4 4 4 4 2 4

Schlechter Status:

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

So viele Antworten und keine Stimmen?
Jesse_b

Werden hier nur Ganzzahlen getestet oder sollten auch Zeichenfolgen getestet werden?
Jesse_b

Ich warte nur auf die beste Antwort, keine Sorge, ich werde bald abstimmen
yael

Ich meinte alle anderen. Diese Frage verdient eine positive Bewertung, IMO.
Jesse_b

Wenn Sie etwas von mindestens dieser Komplexität benötigen, ist es eine gute Zeit, eine echte Programmiersprache zu verwenden, bis es zu spät ist ...
Anzeigename

Antworten:


11

bash+ GNU sort+ GNU- grepLösung:

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

Englische Erklärung: Wenn die eindeutige Sortierung der Elemente des Arrays nur zu einem Element führt, geben Sie "ok" aus. Andernfalls drucken Sie "schlecht".

Das Array wird gedruckt mit NUL - Bytes jedes Element trennt, verrohrt Aussortieren in GNU ( die sich auf dem -zaka --zero-terminatedund -uaka --uniqueOptionen) und dann in grep(unter Verwendung von Optionen -zaka --null-dataund -caka --count) , um die Ausgangsleitungen zu zählen.

Im Gegensatz zu meiner vorherigen Version kann ich diese wcOption nicht verwenden , da Eingabezeilen mit einem Zeilenumbruch abgeschlossen werden müssen ... und die Verwendung von sedoder trzur Konvertierung von NULs in Zeilenumbrüche, nachdem sortdies den Zweck der Verwendung von NUL-Trennzeichen zunichte machen würde. grep -cmacht einen vernünftigen Ersatz.


Hier ist dasselbe, was als Funktion umgeschrieben wurde:

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
Beachten Sie, dass sort -ukeine eindeutigen Elemente zurückgegeben werden, sondern nur eines von jedem Satz von Elementen, die gleich sortiert sind. Zum Beispiel würde es ARRAY_DISK_Quantity=(① ②)auf einem GNU-System "ok" sagen , wo die Gebietsschemata normalerweise entscheiden, dass diese 2 Zeichen gleich sortiert werden. Sie möchten LC_ALL=C sort -ueine Byte-zu-Byte-Eindeutigkeit.
Stéphane Chazelas

nur ein weiterer Hinweis, es wird auch dann fehlschlagen, wenn keine zusätzlichen Festplatten in der CLI
angezeigt werden. Fügen

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

@ StéphaneChazelas das Gebietsschema ist eine Beschäftigung wert, ebenso wie das IFS-Problem. Das Testen auf eine leere Liste wird, IMO, am besten separat durchgeführt - es ist nicht erforderlich, in einer leeren Gruppe nach nicht eindeutigen Elementen zu suchen.
cas

Hallo Cas, ich bevorzuge deine vorherige Antwort
yael

8

Mit zsh:

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

Wo (u)befindet sich ein Parametererweiterungsflag zum Erweitern eindeutiger Werte? Wir erhalten also eine Zählung der eindeutigen Werte im Array.

Ersetzen == 1durch <= 1, wenn Sie davon ausgehen möchten, dass ein leeres Array in Ordnung ist.

Mit ksh93können Sie das Array sortieren und prüfen, ob das erste Element mit dem letzten übereinstimmt:

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

Mit ksh88 oder pdksh / mksh:

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

Mit bashbräuchten Sie wahrscheinlich eine Schleife:

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

(würde mit allen Bourne-ähnlichen Shells mit Array-Unterstützung (ksh, zsh, bash, yash) funktionieren).

Beachten Sie, dass es für ein leeres Array OK zurückgibt. Fügen Sie [ "$#" -gt 0 ] || returnam Anfang der Funktion ein hinzu, wenn Sie das nicht möchten.


alle diese antworten scheinen bash nicht zu unterstützen?
Yael

@yael, siehe Bearbeiten für eine Bash-Lösung. Aber warum würdest du verwenden bash?
Stéphane Chazelas

In Bash steht auf der Hilfeseite für typeset: Obsolete. See `help declare'.Gibt es einen Grund, warum Sie es anstelle von localoder verwenden declare?
wjandrea

1
@wjandrea typesetfunktioniert in allen 4 Shells. Es ist auch das Original von ksh in den frühen 80ern (bash kopierte meistens ksh88, wenn es um das Setzen und Deklarieren von Typen mit variablem Gültigkeitsbereich ging, entschied sich aber, umzubenennen typeset declareund typeseteinen Alias ​​für die Deklaration zu erstellen).
Stéphane Chazelas

4

bash+ awkLösung:

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
}

Testfall 1:

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

Testfall 2:

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

4

Ich habe noch eine einzige Bash-Lösung, die auch mit Strings funktionieren sollte:

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

Demonstration:

[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

Beachten Sie, dass Punkte in Funktionsnamen nicht gültig sind, obwohl Bash ziemlich tolerant ist. Dies kann Probleme wie beim Exportieren der Funktion verursachen.
wjandrea


2

Mit bash und GNU grep:

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

Ja, aber was ist mit (10 10 10)? Ansonsten ganz nett.
Joe

@ Joe: Guter Fang. Ich habe meine Antwort aktualisiert.
Cyrus

1

Hier ist POSIX Awk:

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

0

bash only solution (vorausgesetzt aist 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

Funktioniert, zählt aber alle Fehler, wenn nur einer ausreicht:if [ "$ttt" -ne "$i" ]; then res=1; break; fi;
Joe

0

Verwenden Sie eine for-Schleife, um jedes Array-Element mit dem nächsten zu vergleichen. Beenden Sie die Schleife um eine Iteration weniger als die Länge des Arrays, um zu vermeiden, dass das letzte Element am Ende mit nichts verglichen wird.

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

Willkommen bei U & L und vielen Dank für Ihren Beitrag! Dieser Code gibt "Match" aus, auch wenn eine Nichtübereinstimmung festgestellt wird ... ist dies beabsichtigt?
fra-san
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.