Gruppierte Sortierung fortlaufender Absätze (durch Leerzeichen getrennt)?

8

Ich glaube, ich bin jetzt ziemlich erfahren darin, nach Spalten zu sortieren . Bisher habe ich jedoch noch nichts zum Sortieren fortlaufender Zeilen gefunden .

Angenommen, wir haben eine Textdatei, die so aussieht: (natürlich sehr vereinfacht)

Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

Ist es nun möglich, die Zeilen für jeden Block einzeln alphanumerisch zu sortieren ? Ich meine, damit das Ergebnis so aussieht:

Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Nach dem, was ich in der sortManpage gefunden habe, ist dies mit dem integrierten UNIX- sortBefehl möglicherweise nicht möglich . Oder ist dies sogar möglich, ohne auf externe Tools / Tools von Drittanbietern zurückgreifen zu müssen?

Syntax-Fehler
quelle

Antworten:

9

Dravs awkLösung ist gut, aber das bedeutet, einen sortBefehl pro Absatz auszuführen . Um dies zu vermeiden, können Sie Folgendes tun:

< file awk -v n=0 '!NF{n++};{print n,$0}' | sort -k1n -k2 | cut -d' ' -f2-

Oder Sie könnten das Ganze machen in perl:

perl -ne 'if (/\S/){push@l,$_}else{print sort@l if@l;@l=();print}
          END{print sort @l if @l}' < file

Beachten Sie, dass Trennzeichen oben leere Zeilen sind (für die awkeine Zeile nur Leerzeichen oder Tabulatorzeichen, für die perleine Zeile ein beliebiges horizontales oder vertikales Abstandszeichen) anstelle von leeren Zeilen. Wenn Sie leere Zeilen nicht möchten, können Sie ersetzen !NFmit !lengthoder $0=="", und /\S/mit /./.

Stéphane Chazelas
quelle
Vielen Dank auch an Sie, vor allem für die awkLösung, die den sortOverhead vermeidet ! Hinterhältig!
Syntaxfehler
8
awk -v RS= -v cmd=sort '{print | cmd; close(cmd); print ""}' file

Wenn Sie das Datensatztrennzeichen RSauf eine leere Zeichenfolge setzen, wird in Absätzen jeweils ein Schritt ausgeführt. $0Leiten Sie für jeden Absatz den Absatz (in ) an cmd (auf eingestellt sort) und drucken Sie die Ausgabe. Drucken Sie eine leere Zeile aus, um die ausgegebenen Absätze durch a zu trennen print "".

Wenn wir Perl-Beispiele geben, dann präsentiere ich einen alternativen Ansatz als den von Stephane:

perl -e 'undef $/; print join "\n", sort (split /\n/), "\n" 
    foreach(split(/\n\n/, <>))' < file

Deaktivieren Sie das Feldtrennzeichen ( undef $/), damit wir <>STDIN verwenden und abrufen können. Wir haben dann splitdas um \n\n(Absätze). foreach„Absatz“, sortdie Linien durch splitting um Zeilenumbrüche, sorting und dann joinsie ing wieder zusammen und auf einem hinteren anheftet \n.

Dies hat jedoch einen Nebeneffekt beim Hinzufügen eines Trennzeichens "nachfolgender Absätze" zum letzten Absatz (falls noch kein Trennzeichen vorhanden war). Sie können das mit dem etwas weniger hübschen umgehen:

perl -e 'undef $/; print join "\n", sort (split /\n/) , (\$_ == \$list[-1] ? "" : "\n")
    foreach(@list = split(/\n\n/, <>))' < file

Dies weist die Absätze zu @list, und dann gibt es eine "ternäre Operation", um zu überprüfen, ob es das letzte Element der foreach(der \$_ == \$list[-1]Prüfung) ist. print ""wenn es ( ? ...) ist, else ( : ...) print "\n"für alle anderen "Absätze" (Elemente von @list).

Drav Sloan
quelle
Das ist ordentlich! Vielen Dank. Rufen Sie tatsächlich /usr/bin/sortmit dieser Zeile auf oder handelt es sich um einen awkintegrierten "Sortier" -Befehl?
Syntaxfehler
Aufruf des
Sortierbefehls
5

Ich habe ein Tool in haskell geschrieben, mit dem Sie sort, shuf, tac oder einen anderen Befehl für Textabschnitte verwenden können.

https://gist.github.com/siers/01306a361c22f2de0122
BEARBEITEN: Das Tool ist auch in diesem Repo enthalten: https://github.com/siers/haskell-import-sort

Es teilt den Text in Blöcke auf, verbindet die Unterblöcke mit \0char, leitet den Befehl weiter und macht schließlich das Gleiche in umgekehrter Reihenfolge.

28-08-2015 : Ich habe einen anderen persönlichen Gebrauch für dieses Tool gefunden - N Absätze nach einer Zeile auswählen.

paramap grep -aA2 '^reddit usernames' < ~/my-username-file
reddit usernames

foo
bar
baz

a couple
more of these
Raitis Veinbahs
quelle
4

Wenn Sie GNU awk zur Verfügung haben, können Sie jeden Block mit der integrierten asort()Funktion sortieren . Etwas wie das:

blocksort.awk

function sort_n_print(array) {
  asort(array)
  for(i=1; i<=length(array); i++)
    print array[i]
  delete array
}

NF { a[++x] = $0 }

!NF { sort_n_print(a); print }

END { sort_n_print(a) }

Führen Sie es so aus:

awk -f blocksort.awk infile
Thor
quelle
1

TXR Lisp Schritt für Schritt:

$ cat data
Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

$ txr -p '(get-lines)' < data
("Echo" "Alpha" "Delta" "Charlie" "" "Golf" "Bravo" "Hotel" "Foxtrot")

$ txr -t '(get-lines)' < data
Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

$ txr -p '(partition* (get-lines) (op where [chain length zerop]))' < data
(("Echo" "Alpha" "Delta" "Charlie") ("Golf" "Bravo" "Hotel" "Foxtrot"))

$ txr -p '[mapcar sort (partition* (get-lines) (op where [chain length zerop]))]' < data
(("Alpha" "Charlie" "Delta" "Echo") ("Bravo" "Foxtrot" "Golf" "Hotel"))

$ txr -p '(interpose (list "") [mapcar sort (partition* (get-lines) (op where [chain length zerop]))])' < data
(("Alpha" "Charlie" "Delta" "Echo") ("") ("Bravo" "Foxtrot" "Golf" "Hotel"))

$ txr -t '(interpose (list "") [mapcar sort (partition* (get-lines) (op where [chain length zerop]))])' < data
Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Referenzen: get-lines , partition * , op , where , chain , length , zerop , mapcar , interpose .

Kaz
quelle
Beachten Sie, dass [mapcar sort ...]wir in der sortdurch eine Funktion ersetzen könnten, die die Zeichenfolgen durch einen externen Prozess leitet. Wir können dann ein Werkzeug zum Verteilen eines externen Textverarbeitungsbefehls über Absätze erhalten.
Kaz