web-dev-qa-db-de.com

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

Ich habe fast die gleiche Frage wie hier .

Ich habe ein Array, das aa ab aa ac aa ad, etc ..__ enthält. Jetzt möchte ich alle eindeutigen Elemente aus diesem Array auswählen. __ Ich denke, das wäre einfach mit sort | uniq oder mit sort -u, wie sie in der anderen Frage erwähnt wurden, aber nichts geändert im Array ....__ Der Code lautet:

echo `echo "${ids[@]}" | sort | uniq`

Was mache ich falsch?

60
Jetse

Ein bisschen hackig, aber das sollte es tun:

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

Um die sortierten eindeutigen Ergebnisse wieder in einem Array zu speichern, machen Sie Array-Zuordnung :

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

Wenn Ihre Shell herestrings (bash sollte) unterstützt, können Sie einen echo-Prozess durch Ändern in Folgendes ersetzen:

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

Eingabe:

ids=(aa ab aa ac aa ad)

Ausgabe:

aa ab ac ad

Erklärung:

  • "${ids[@]}" - Syntax für das Arbeiten mit Shell-Arrays, unabhängig davon, ob sie als Teil von echo oder als Herestring verwendet wird. Der @-Teil bedeutet "alle Elemente im Array".
  • tr ' ' '\n' - Wandelt alle Leerzeichen in Zeilenumbrüche um. Weil Ihr Array von Shell als Elemente in einer einzelnen Zeile betrachtet wird, die durch Leerzeichen getrennt sind. und weil sort erwartet, dass die Eingabe in separaten Zeilen erfolgt.
  • sort -u - sortieren und behalten Sie nur eindeutige Elemente
  • tr '\n' ' ' - konvertiert die neu hinzugefügten Zeilenumbrüche in Leerzeichen.
  • $(...) - Befehlsersetzung
  • Nebenbei: tr ' ' '\n' <<< "${ids[@]}" ist eine effizientere Methode: echo "${ids[@]}" | tr ' ' '\n'
93
sampson-chen

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

$ 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, da in einem Array jeder Schlüssel nur einmal angezeigt werden kann. Wenn die for-Schleife den zweiten Wert von aa in a[2] erreicht, überschreibt sie b[aa], der ursprünglich für a[0] festgelegt wurde.

Dinge in nativen Bash zu machen kann schneller sein als die Verwendung von Pipes und externen Tools wie sort und uniq.

Wenn Sie sich sicher fühlen, können Sie die for-Schleife umgehen, indem Sie die Möglichkeit der printf verwenden, um das Format für mehrere Argumente zu verwenden. Dies erfordert jedoch eval. (Hör jetzt auf zu lesen, wenn es dir gut geht.)

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

Der Grund für diese Lösung, dass eval erforderlich ist, besteht darin, dass Arraywerte vor der Wortaufteilung bestimmt werden. Das bedeutet, dass die Ausgabe der Befehlsersetzung als ein einzelnes Wort betrachtet wird, und nicht als Satz von Schlüssel = Wert-Paaren.

Während dies eine Subshell verwendet, werden für die Verarbeitung der Array-Werte nur Bash-Builtins verwendet. Achten Sie darauf, Ihre Verwendung von eval mit einem kritischen Auge zu bewerten. Wenn Sie nicht zu 100% sicher sind, dass chepner oder glenn jackman oder greycat keinen Fehler in Ihrem Code finden, verwenden Sie stattdessen die for-Schleife.

22
ghoti

Wenn Ihre Array-Elemente Leerzeichen oder ein anderes Shell-Sonderzeichen haben (und können Sie sicher sein, dass sie dies nicht tun?), Dann müssen Sie diese zuerst erfassen (und Sie sollten dies immer tun), indem Sie Ihr Array in Anführungszeichen setzen. z.B. "${a[@]}". Bash interpretiert dies wörtlich als "jedes Arrayelement in einem separaten argument ". Innerhalb von bash funktioniert das einfach immer, immer.

Um ein sortiertes (und eindeutiges) Array zu erhalten, müssen wir es in ein Format sortieren, das verständlich ist, und es in der Lage sein, es wieder in bash-Array-Elemente zu konvertieren. Das ist das Beste, was ich mir ausgedacht habe:

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

Leider schlägt dies im Sonderfall des leeren Arrays fehl, wodurch das leere Array in ein Array mit einem leeren Element umgewandelt wird (da printf 0 Argumente hatte, aber immer noch gedruckt wird, als hätte es ein leeres Argument - siehe Erläuterung). Also musst du das in einem Wenn oder Etwas fangen.

Erläuterung: Das% q-Format für printf "Shell entgeht" dem gedruckten Argument, und zwar so, dass bash sich in etwas wie eval! .__ erholen kann. Da jedes Element gedruckt wird, wird Shell nur in einer eigenen Zeile angezeigt Trennzeichen zwischen Elementen ist der Zeilenvorschub, und die Array-Zuweisung übernimmt jede Zeile als Element und analysiert die mit Escapezeichen versehenen 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 den Escape von jedem Wert in das Array zu entfernen.

11
vontrapp

Ich weiß, dass dies bereits beantwortet wurde, aber es zeigte sich ziemlich hoch in den Suchergebnissen und es 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
~>
8
das.cyklone

'sort' kann verwendet werden, um die Ausgabe einer for-Schleife zu sortieren:

for i in ${ids[@]}; do echo $i; done | sort

duplikate mit "-u" entfernen und entfernen:

for i in ${ids[@]}; do echo $i; done | sort -u

Schließlich können Sie Ihr Array einfach mit den einzigartigen Elementen überschreiben:

ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )
7
corbyn42

dieses wird auch die Ordnung bewahren: 

echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'

und das ursprüngliche Array mit den eindeutigen Werten ändern:

ARRAY=($(echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'))
2
faustus

Um ein neues Array zu erstellen, das aus eindeutigen Werten besteht, stellen Sie sicher, dass Ihr Array nicht leer ist, und führen Sie einen der folgenden Schritte aus:

Doppelte Einträge entfernen (mit Sortierung)

readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | sort -u)

Doppelte Einträge entfernen (ohne zu sortieren)

readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | awk '!x[$0]++')

Warnung: Versuchen Sie nicht, etwas wie NewArray=( $(printf '%s\n' "${OriginalArray[@]}" | sort -u) ) zu tun. Es wird auf Leerzeichen brechen.

2
Six

cat number.txt 

1 2 3 4 4 3 2 5 6

zeile in Spalte drucken: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' 

1
2
3
4
4
3
2
5
6

finden Sie die doppelten Datensätze: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk 'x[$0]++' 

4
3
2

Ersetzen Sie doppelte Datensätze: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk '!x[$0]++' 

1
2
3
4
5
6

Nur Uniq-Datensätze suchen: cat number.txt | awk '{for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}

1
5
6
1
VIPIN KUMAR

Wenn Sie eine Lösung suchen, die nur Bash-Interns verwendet, können Sie die Werte als Schlüssel in einem assoziativen Array festlegen und dann die Schlüssel extrahieren:

declare -A uniqs
list=(foo bar bar "bar none")
for f in "${list[@]}"; do 
  uniqs["${f}"]=""
done

for thing in "${!uniqs[@]}"; do
  echo "${thing}"
done

Dies wird ausgegeben

bar
foo
bar none
1
rln

Versuchen Sie dies, um eindeutige Werte für die erste Spalte in der Datei zu erhalten

awk -F, '{a[$1];}END{for (i in a)print i;}'
0
Suresh Aitha

Ohne die ursprüngliche Bestellung zu verlieren:

uniques=($(tr ' ' '\n' <<<"${original[@]}" | awk '!u[$0]++' | tr '\n' ' '))
0
estani