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 vonecho
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 behaltentr '\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'