GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wie kann ich eindeutige Werte aus einem Array in Bash erhalten?

Mir ist klar, dass dies bereits beantwortet wurde, aber es wurde ziemlich weit oben in den Suchergebnissen angezeigt und könnte jemandem helfen.

printf "%s\n" "${IDS[@]}" | sort -u

Beispiel:

~> IDS=( "aa" "ab" "aa" "ac" "aa" "ad" )
~> echo  "${IDS[@]}"
aa ab aa ac aa ad
~>
~> printf "%s\n" "${IDS[@]}" | sort -u
aa
ab
ac
ad
~> UNIQ_IDS=($(printf "%s\n" "${IDS[@]}" | sort -u))
~> echo "${UNIQ_IDS[@]}"
aa ab ac ad
~>

Wenn Ihre Array-Elemente Leerzeichen oder andere Shell-Sonderzeichen enthalten (und können Sie sicher sein, dass dies nicht der Fall ist?), Um diese zuerst zu erfassen (und Sie sollten dies einfach immer tun), drücken Sie Ihr Array in doppelten Anführungszeichen aus! z.B. "${a[@]}" . Bash interpretiert dies buchstäblich als „jedes Array-Element in einem separaten Argument ". Innerhalb von Bash funktioniert das einfach immer, immer.

Um dann ein sortiertes (und eindeutiges) Array zu erhalten, müssen wir es in ein Format konvertieren, das sort versteht, und in der Lage sein, es wieder in Bash-Array-Elemente zu konvertieren. Das ist das Beste, was mir eingefallen ist:

eval a=($(printf "%q\n" "${a[@]}" | sort -u))

Leider schlägt dies im Sonderfall des leeren Arrays fehl und verwandelt das leere Array in ein Array mit 1 leeren Element (weil printf 0 Argumente hatte, aber immer noch so druckt, als hätte es ein leeres Argument - siehe Erklärung). Also musst du das in einem if oder so abfangen.

Erklärung:Das %q-Format für printf „shell-escaped“ das gedruckte Argument, genau so, wie bash in etwas wie eval wiederhergestellt werden kann! , und die Array-Zuweisung nimmt jede Zeile als Element und parst die Escape-Werte in wörtlichen Text.

z. B.

> a=("foo bar" baz)
> printf "%q\n" "${a[@]}"
'foo bar'
baz
> printf "%q\n"
''

Das eval ist notwendig, um das Escapezeichen von jedem Wert zu entfernen, der in das Array zurückgeht.


Wenn Sie Bash Version 4 oder höher ausführen (was in jeder modernen Version von Linux der Fall sein sollte), können Sie eindeutige Array-Werte in Bash erhalten, indem Sie ein neues assoziatives Array erstellen, das alle Werte des ursprünglichen Arrays enthält. Etwa so:

$ a=(aa ac aa ad "ac ad")
$ declare -A b
$ for i in "${a[@]}"; do b["$i"]=1; done
$ printf '%s\n' "${!b[@]}"
ac ad
ac
aa
ad

Dies funktioniert, weil in jedem Array (assoziativ oder traditionell, in jeder Sprache) jeder Schlüssel nur einmal vorkommen kann. Wenn der for Schleife kommt beim zweiten Wert von aa an in a[2] , es überschreibt b[aa] die ursprünglich für a[0] eingestellt war .

Dinge in der nativen Bash zu erledigen, kann schneller sein als die Verwendung von Pipes und externen Tools wie sort und uniq , obwohl Sie bei größeren Datensätzen wahrscheinlich eine bessere Leistung sehen werden, wenn Sie eine leistungsfähigere Sprache wie awk, python usw. verwenden.

Wenn Sie sich sicher fühlen, können Sie for vermeiden Schleife mit printf die Fähigkeit von , sein Format für mehrere Argumente wiederzuverwenden, obwohl dies anscheinend eval erfordert . (Hören Sie jetzt auf zu lesen, wenn Sie damit einverstanden sind.)

$ eval b=( $(printf ' ["%s"]=1' "${a[@]}") )
$ declare -p b
declare -A b=(["ac ad"]="1" [ac]="1" [aa]="1" [ad]="1" )

Der Grund, warum diese Lösung eval erfordert besteht darin, dass Array-Werte vor der Wortaufteilung bestimmt werden. Das bedeutet, dass die Ausgabe der Befehlsersetzung als ein einzelnes Wort betrachtet wird statt einer Reihe von Schlüssel=Wert-Paaren.

Während dies eine Subshell verwendet, verwendet es nur Bash-Builts, um die Array-Werte zu verarbeiten. Stellen Sie sicher, dass Sie Ihre Verwendung von eval bewerten mit kritischem Blick. Wenn Sie nicht hundertprozentig sicher sind, dass Chepner, Glenn Jackman oder Greycat keinen Fehler in Ihrem Code finden würden, verwenden Sie stattdessen die for-Schleife.


Ein bisschen hacky, aber das sollte es tun:

echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '

Um die sortierten eindeutigen Ergebnisse wieder in einem Array zu speichern, führen Sie eine Array-Zuweisung durch:

sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))

Wenn Ihre Shell Herestrings unterstützt (bash sollte), können Sie echo entbehren verarbeiten, indem Sie es ändern in:

tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '

Ein Hinweis vom 28. August 2021:

Laut ShellCheck Wiki 2207 a read -a Pipe sollte verwendet werden, um Splitting zu vermeiden. In Bash würde der Befehl also lauten:

IFS=" " read -r -a ids <<< "$(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')"

oder

IFS=" " read -r -a ids <<< "$(tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' ')"

Eingabe:

ids=(aa ab aa ac aa ad)

Ausgabe:

aa ab ac ad

Erklärung:

  • "${ids[@]}" - Syntax zum Arbeiten mit Shell-Arrays, ob als Teil von echo verwendet oder ein Herestring. Die @ part bedeutet "alle Elemente im Array"
  • tr ' ' '\n' - Wandeln Sie alle Leerzeichen in Zeilenumbrüche um. Weil Ihr Array von der Shell als Elemente in einer einzelnen Zeile gesehen wird, die durch Leerzeichen getrennt sind; und weil sort erwartet, dass die Eingabe in separaten Zeilen erfolgt.
  • sort -u - Nur eindeutige Elemente sortieren und behalten
  • tr '\n' ' ' - Wandeln Sie die Zeilenumbrüche, die wir zuvor hinzugefügt haben, wieder in Leerzeichen um.
  • $(...) - Befehlsersetzung
  • Beiseite:tr ' ' '\n' <<< "${ids[@]}" ist eine effizientere Methode:echo "${ids[@]}" | tr ' ' '\n'

Linux
  1. Wie erstelle ich ein Array eindeutiger Elemente aus einer Zeichenfolge/einem Array in Bash?

  2. Alle Dateien außer den Dateien im Array abrufen – Bash?

  3. Wie überprüfe ich, ob Bash Farben drucken kann?

  4. Wie kann ich Docker-Linux-Containerinformationen aus dem Container selbst abrufen?

  5. Wie bekomme ich den Hostnamen von der IP (Linux)?

Bash Break:So verlassen Sie eine Schleife

So deklarieren Sie ein 2D-Array in Bash

Wie kann ich ein Verzeichnis vom Befehl ls ausschließen

Wie bekomme ich Elemente aus der Liste in Bash?

Wie kann ich die Länge einer Videodatei von der Konsole abrufen?

So erhalten Sie den Benutzernamen von uid