Wie erstelle ich ein Array eindeutiger Elemente aus einem String / Array in Bash?


8

Wenn ich eine Zeichenfolge "1 2 3 2 1" oder ein Array [1,2,3,2,1] habe, wie kann ich die eindeutigen Werte auswählen, d. H.

"1 2 3 2 1" produces "1 2 3" 

oder

[1,2,3,2,1] produces [1,2,3]

Ähnlich wie uniq, aber uniq scheint auf ganzen Linien zu funktionieren, nicht auf Mustern innerhalb einer Linie ...

Antworten:


4

Mit GNU awk(dies behält auch die ursprüngliche Reihenfolge bei)

printf '%s\n' "1 2 3 2 1" | awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}'
1 2 3 

In readein bashArray

read -ra arr<<<$(printf '%s\n' "1 2 3 2 1" |
 awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}')
printf "%s\n"  "${arr[@]}"
1
2
3

Wie kann ich das dann zu einem Array machen?
Michael Durrant

@ MichaelDurrant, wenn Sie ein bashArray meinen , fügte einen Weg hinzu
iruvar

Sehen Sie hier, ob Ihr Array Leerzeichen enthält
Tom Hale

@iruvar kannst du bitte erklären was das eigentlich bedeutet? Ich bin neu in awk scripting und es wäre hilfreich, wenn Sie klären könnten, was tatsächlich passiert, wenn Sie dies sagen! a [$ 0] ++
Abhishek

@iruvar Wenn es nicht möglich ist, in Kommentaren eine Website zu erklären, die zumindest die obige Syntax erklärt, wäre dies von Vorteil.
Abhishek

9

Wenn Sie zsh verwenden:

$ array=(1 2 3 2 1)
$ echo ${(u)array[@]}
1 2 3

oder (wenn die KSH_ARRAYSOption nicht eingestellt ist) sogar

$ echo ${(u)array}
1 2 3

1
Wenn das Array leere Elemente enthalten kann, sollten Sie stattdessen "${(u)array[@]}"oder "${(@u)array}"verwenden (beachten Sie die Anführungszeichen).
Stéphane Chazelas

Ich verwende zsh 5.1.1 (x86_64-ubuntu-linux-gnu) und ${(u)array}arbeite auch dann, wenn das Array leer ist oder eine leere Zeichenfolge ohne Anführungszeichen enthält.
Kiamlaluno

4

Für ein Array mit beliebigen Werten ist es ziemlich schwierig, bashda es keinen eingebauten Operator dafür hat.

bash Das Speichern von NUL-Zeichen in seinen Variablen wird jedoch nicht unterstützt. Sie können dies also verwenden, um dies an andere Befehle zu übergeben:

Das Äquivalent von zsh's:

new_array=("${(@u}array}")

auf einem neueren GNU-System könnte sein:

eval "new_array=($(
  printf "%s\0" "${array[@]}" |
    LC_ALL=C sort -zu |
    xargs -r0 bash -c 'printf "%q\n" "$@"' sh
  ))"

Alternativ können Sie in neueren Versionen von bashund unter der Annahme, dass keines der Array-Elemente leer ist, assoziative Arrays verwenden:

unset hash
typeset -A hash
for i in "${array[@]}"; do
  hash[$i]=
done
new_array=("${!hash[@]}")

Mit Bash 4.4 und neuer und mit GNU sort:

readarray -td '' new_array < <(
  printf '%s\0' "${array[@]}" | LC_ALL=C sort -zu)

Die Reihenfolge der Elemente wäre in diesen verschiedenen Lösungen nicht gleich.

Mit tcsh:

set -f new_array = ($array:q)

Würde behalten die f irst Element ( a b a=> a b) , wie zsh‚s (u)Expansion Flagge.

set -l new_array = ($array:q)

Würde das letzte ( a b a=> b a) behalten . Diese entfernen jedoch leere Elemente aus dem Array.


1

Diese Lösung hat bei mir funktioniert.

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

Das Obige erzeugt 1 2 3 als Ausgabe.

Kürzere Version, wie von Costas vorgeschlagen, könnte sein,

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

Um die Endergebnisse in einem Array zu speichern, können Sie Folgendes tun:

IFS=$' '
arr=($(printf "%s\n" "${ids[@]}" | sort -u | tr '\n' ' '))
unset IFS

Wenn ich jetzt ein Echo mache arr, ist dies die Ausgabe, die ich bekomme.

echo "${arr[@]}"
1 2 3

Verweise

https://stackoverflow.com/a/13648438/1742825 https://stackoverflow.com/a/9449633/1742825


@ Costas, danke. Ich habe es in die Antwort aufgenommen.
Ramesh

Wie kann ich das Endergebnis zu einem Array machen?
Michael Durrant

@ MichaelDurrant, bitte lesen Sie die aktualisierte Antwort und lassen Sie mich wissen, ob dies in Ordnung ist.
Ramesh

Wenn Sie das Ergebnis in ein Array tr '\n' ' '
Costas

0

Um es vollständig in der Shell zu tun und das Ergebnis in ein Array zu setzen,

declare -A seen
for word in one two three two one
do
        if [ ! "${seen[$word]}" ]
        then
                result+=("$word")
                seen[$word]=1
        fi
done
echo "${result[@]}"

In Worten: Wenn wir ein bestimmtes Wort noch nicht gesehen haben, fügen Sie es dem resultArray hinzu und markieren Sie es als gesehen. Sobald ein Wort gesehen wurde, ignorieren Sie nachfolgende Erscheinungen.


2
Beachten Sie, dass Sie unset seenzuvor einen declare -A seenFall benötigen, der $seenzuvor definiert wurde (auch als skalare Variable aus der Umgebung).
Stéphane Chazelas
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.