
Können Sie mir bitte einen Vorschlag machen, wie ich eine eindeutige Sortierung aus einer Zeile oder einem Satz machen kann? Ich habe Informationen wie diese:
Special c1,c2,c5,c7,c1,c2
Special2 C6
(das ist ein TAB-Zeichen zwischen Special
und c1...
).
Ich möchte die Ausgabe folgendermaßen:
Special c1,c2,c5,c7
Special2 C6
Wie kann ich das erreichen?
Antwort1
Verwenden vondiese Antwort,
perl -MList::MoreUtils=uniq -laF'\t' -ne '
$F[1] = join(",", uniq(sort(split(",", $F[1])))); print join("\t", @F)'
Dies hängt von einem externen Paket abList::WeitereDienstprogrammeWenn Sie keine externe Abhängigkeit installieren möchten, uniq
ist die Neuimplementierung der Funktionnur noch ein paar Zeilen Perl. (Obwohl es bei mir anscheinend als Teil des Basissystems auf macOS installiert ist.)
Antwort2
perl -F'\t|,' -lane 'my %h; print shift @F, "\t", join ",", sort grep !$h{$_}++, @F' dataf
Erläuterung
-F'\t|,'
=> teilt jedes Datensatzfeld in das Array@F
nachTAB
Zeichen aufcomma
.-l
wird auch „RS
zunewline
“ und „ORS
bis“ festlegen .newline
-a
teilt jeden Datensatz automatisch in Wörter auf, basierend auf derFS
Auswahl von-F
.-n
richtet eine implizite Datensatz-Leseschleife für die Eingabe einAND
und druckt Dinge nur auf Aufforderung.-e
ist derPerl
Code, der für jeden Datensatz der Eingabe basierend auf der oben getroffenenRS
Auswahl ausgeführt werden soll-l
.- Das erste Element wird durch angegeben
shift
und die restlichen Elementeuniquified
durch Speichern als Schlüssel eines Hashs,%h
der bei jedem Einlesen eines Datensatzes neu generiert werden soll. Die eindeutigen Elemente werden dann sortiert, mit einem Komma verbunden und gedruckt.
Antwort3
Getestet mit OpenBSD awk
, GNU awk
und mawk
:
awk -F ',| +' '{ for (i = 2; i <= NF; ++i) { print $1, $i } }' data.in |
sort -u |
awk '{ f[$1] = (f[$1] ? f[$1] "," : "") $2 } END { for (k in f) { print k, f[k] } }'
Die erste awk
erweitert die gegebenen Daten in
Special c1
Special c2
Special c5
Special c7
Special c1
Special c2
Special2 C6
Es verwendet sowohl Kommas als auch mehrere Leerzeichen als Feldtrennzeichen und druckt für jeden Datensatz (Zeile) der Eingabe das erste Feld, gefolgt von jedem der anderen Felder nacheinander in separaten Zeilen. Dies setzt voraus, dass in den Zeilen keine anderen Leerzeichen oder Kommas vorhanden sind als dort, wo sie richtig als Trennzeichen interpretiert werden.
Der sort
in der Mitte sortiert es in
Special2 C6
Special c1
Special c2
Special c5
Special c7
Es führt eine Sortierung durch, wobei die vollständige Zeile als Sortierschlüssel verwendet wird, und verwirft alle doppelten Zeilen.
Der letzte awk
rekombiniert die Daten in
Special c1,c2,c5,c7
Special2 C6
Dies geschieht, indem das erste Feld als Schlüssel in ein assoziatives Array verwendet wird und die durch Kommas getrennte Verkettung der entsprechenden Daten im zweiten Feld als Wert gespeichert wird. Am Ende werden alle gesammelten Daten ausgedruckt.
Antwort4
Ein anderer Weg in einer Zeile:
while read line; do echo "$line" | awk '{print $1}' | tr '\n' ' '; echo "$line" | awk '{print $2}' | tr ',' '\n' | sort -u | tr '\n' ',' | sed -e 's/.$//g'; echo; done < file_to_sort
Es nimmt die erste Spalte jeder Zeile ( echo $line | awk '{print $1}' | tr '\n' ' ';
) und sortiert die Werte der zweiten Spalte, getrennt durch „,“, nachdem es sie zur Anwendung in eine einzelne Spalte umgewandelt sort
und sie dann wieder in eine einzelne Zeile mit der ursprünglichen Formatierung zurückkonvertiert hat ( echo $line | awk '{print $2}' | tr ',' '\n' | sort -u | tr '\n' ','
).
Zeilenaufteilung wie von @tripleee vorgeschlagen durchführen:
while IFS=$'\t' read first second; do printf "%s\t%s\n" "$first" "$(tr ',' '\n' <<<"$second" | sort | tr '\n' ',' | sed -e 's/.$//g';)"; done < file_to_sort