Sortieren Sie Textdateien mit mehreren Zeilen als Zeile

13

Ich habe eine Textdatei in diesem Format:

####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Ich möchte diese Datei KEYzeilenweise sortieren und die nächsten 4 Zeilen mit dem Ergebnis belassen, also sollte das sortierte Ergebnis lauten:

####################################
KEY1
VAL11
VAL12
VAL13
VAL14
####################################
KEY2
VAL21
VAL22
VAL23
VAL24
####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Gibt es eine Möglichkeit, dies zu tun?

RYN
quelle
5
Bitte nicht überqueren
Zanna
@Zanna: Ich denke, es gibt einen Ausschluss für Unix- und Askubuntu-Abschnitte, da sich diese beiden Bereiche stark überschneiden! Ich glaube, ich habe darüber in der Meta-Sektion von Unix gelesen
RYN
2
relevante Meta-Frage, die hier von AU mod gestellt wurde :) Wie sollen auf Ask Ubuntu gekreuzte Fragen behandelt werden?
Zanna
@RYN Das Problem wäre nicht die Überlappung, tatsächlich überlappen sich viele SE-Sites, aber die Personen, die Antworten geben, wissen möglicherweise nichts über die Antworten auf der anderen Site.
Phk

Antworten:

12

msort(1)wurde entwickelt, um Dateien mit mehrzeiligen Datensätzen sortieren zu können. Es verfügt über eine optionale Benutzeroberfläche sowie eine normale und für Menschen verwendbare Befehlszeilenversion. (Zumindest Menschen, die Handbücher gerne sorgfältig lesen und nach Beispielen suchen ...)

AFAICT, Sie können kein beliebiges Muster für Datensätze verwenden, es sei denn, Ihre Datensätze haben eine feste Größe (in Byte, keine Zeichen oder Zeilen). msorthat eine -bOption für Datensätze, die durch Leerzeilen getrennte Zeilenblöcke sind.

Sie können Ihre Eingabe in ein Format umwandeln, das -bproblemlos funktioniert , indem Sie vor jede Zeile ###...(mit Ausnahme der ersten) eine Leerzeile einfügen .

Standardmäßig werden Statistiken zu stderr ausgegeben. Zumindest ist es leicht zu erkennen, wann die Sortierung fehlgeschlagen ist, da die gesamte Eingabe für einen einzelnen Datensatz gehalten wurde.


msortarbeitet an Ihren Daten. Mit dem sedBefehl wird jeder #+Zeile mit Ausnahme von Zeile 1 eine neue Zeile vorangestellt. -wDer gesamte Datensatz wird (lexikografisch) sortiert. Es gibt Optionen zum Auswählen des Teils eines Datensatzes, der als Schlüssel verwendet werden soll, aber ich habe sie nicht benötigt.

Ich habe auch die zusätzlichen Zeilenumbrüche weggelassen.

$ sed '2,$ s/^#\+/\n&/' unsorted.records | msort -b -w 2>/dev/null 
####################################
KEY1
VAL11
VAL12
VAL13
VAL14

####################################
KEY2
VAL21
VAL22
VAL23
VAL24

####################################
KEY3
VAL31
VAL32
VAL33
VAL34

Ich hatte kein Glück damit -r '#', das als Datensatztrennzeichen zu verwenden. Es dachte, die ganze Datei sei ein Datensatz.

Peter Cordes
quelle
vielen Dank; msortist sehr nützlich; danke (etwa -rscheint es, weil es mehr als eine # gibt, die ich verwendet habe -dund es hat funktioniert
RYN
cool! (+1) msort -qwr '#' ex funktioniert bei mir (naja, es chagt den Output Rec. Separator)
JJoao
8

Eine Lösung besteht darin, zuerst die Zeilenumbrüche innerhalb eines Blocks in ein nicht verwendetes Zeichen Ihrer Wahl zu ändern ('|' im folgenden Beispiel), das Ergebnis zu sortieren und das ausgewählte Trennzeichen auf den ursprünglichen Zeilenumbruchs zurückzusetzen:

sed -e 'N; N; N; N; N; s/\n/|/g' file.txt \
| sort -k2,2 -t\| \
| sed 's/|/\n/g'
xhienne
quelle
1
Vielen Dank; das funktioniert, ist aber sehr schmutzig, besonders wenn die Daten auch schmutzig sind! Wenn die Zeilen nach dem Schlüssel 100 waren, muss ich ;Ndort 100 setzen , und es kann schwierig werden, ein Zeichen zu finden, das nicht im Text selbst verwendet wird. Es ist sehr gut für sortoder awk... in der Lage sein, mehrzeilige Sortierung
RYN
4
perl -0ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0 schlürft die gesamte Datei
  • /(....)/g Ordne die Datensätze zu und extrahiere sie
  • print sort ... sortiere und drucke sie
Joao
quelle
2

Hier ist eine andere Möglichkeit, die mit einer beliebigen Anzahl von Zeilen in einem KEYAbschnitt funktionieren sollte :

# extract delimiter
delim=$(head -n1 <infile)
sed '/#/d;/KEY/h;G;s/\n/\x02/' infile | nl -ba -nrz -s $'\002' | sort -t $'\002' -k3 -k1,1 |
cut -d $'\002' -f2 | sed '/KEY/{x;s/.*/'"${delim}"'/;G}'

Dies funktioniert, indem der Begrenzer in einer Variablen gespeichert wird (um ihn dann aus der Eingabe zu entfernen). Anschließend wird KEY*an jede Zeile in dem entsprechenden Abschnitt das ASCII-Zeichen angehängt , wobei ein niedriges nASCII- lZeichen als Trennzeichen verwendet wird (das in Ihrer Eingabe wahrscheinlich nicht vorkommt). Anschließend werden alle Zeilen mit demselben Trennzeichen umnummeriert. Es ist dann nur noch eine Frage des sort3. und 1. Feldes undcut durchlaufen die mittlere Spalte zu tönen und dann die Begrenzer über ein Finale wiederherzustellen sed. Beachten Sie, dass bei den oben genannten KEY12Sortiervorgängen KEY2der sortBefehl entsprechend Ihren Anforderungen angepasst wird.

don_crissti
quelle
2

Sie können die POSIX Awk stdlib-Bibliothek verwenden :

#!/usr/local/bin/awklib -f
$0 ~ "#" {x++}
{q[x] = q[x] ? q[x] RS $0 : $0}
END {
  arr_sort(q)
  for (x in q) print q[x]
}
Steven Penny
quelle