Ordinamento raggruppato di paragrafi continui (separati da una riga vuota)?


8

Penso di essere abbastanza esperto ora nell'ordinamento per colonne ; tuttavia, finora non ho trovato nulla su come ordinare le file continue .

Supponiamo di avere un file di testo simile al seguente: (molto semplificato, ovviamente)

Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

Ora, è possibile ordinare le linee alfanumericamente per ciascun blocco separatamente ? Voglio dire, in modo che il risultato sia simile al seguente:

Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Raccontando ciò che ho trovato nella sortpagina man, questo potrebbe non essere possibile con il sortcomando UNIX incorporato . O può anche essere fatto senza ricorrere a strumenti esterni / di terze parti?

Risposte:


9

La awksoluzione di Drav è buona, ma ciò significa eseguire un sortcomando per paragrafo. Per evitarlo, potresti fare:

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

Oppure potresti fare tutto in perl:

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

Si noti che sopra, i separatori sono linee vuote (per l' awkuna, linee con solo spazio o caratteri di tabulazione, per l' perluna, qualsiasi carattere di spaziatura orizzontale o verticale) anziché linee vuote. Se si desidera linee vuote, è possibile sostituire !NFcon !lengtho $0==""e /\S/con /./.


Grazie anche a te, soprattutto per la awksoluzione che evita le sortspese generali! Subdolo!
syntaxerror,

9
awk -v RS= -v cmd=sort '{print | cmd; close(cmd); print ""}' file

L'impostazione del separatore di record RSsu una stringa vuota fa un passaggio di awk nei paragrafi alla volta. Per ogni paragrafo, reindirizzare il paragrafo (in $0) a cmd (che è impostato su sort) e stampare l'output. Stampa una riga vuota per separare i paragrafi di output con a print "".

Se diamo esempi perl, presento un approccio alternativo rispetto a quello di Stephane:

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

Disinserire il separatore di campo ( undef $/), questo ci consente di utilizzare <>e ottenere l'intero STDIN. Abbiamo poi splitquello intorno \n\n(paragrafi). foreach"paragrafo", sortle linee splitruotando attorno a nuove linee, sorting e poi joinrimettendole insieme e virando su un finale \n.

Tuttavia, ciò ha un effetto collaterale nell'aggiungere un separatore "paragrafo finale" sull'ultimo paragrafo (se non ne aveva uno prima). Puoi aggirare quello con il leggermente meno carino:

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

Questo assegna i paragrafi a @list, e quindi c'è una "operazione ternaria" per verificare se è l'ultimo elemento del foreach(il \$_ == \$list[-1]controllo). stampa ""se è ( ? ...), else ( : ...) stampa "\n"per tutti gli altri "paragrafi" (elementi di @list).


Questo è pulito! Grazie. In realtà stai invocando /usr/bin/sortcon quella linea o è un awkcomando "sort" incorporato?
syntaxerror

Richiamare il comando sort, quindi l'obbligo di chiudere (cmd) su ciascun loop :)
Drav Sloan

5

Ho scritto uno strumento in haskell che ti permette di usare sort, shuf, tac o qualsiasi altro comando su paragrafi di testo.

https://gist.github.com/siers/01306a361c22f2de0122
EDIT: lo strumento è incluso anche in questo repository: https://github.com/siers/haskell-import-sort

Suddivide il testo in blocchi, unisce i sottoblocchi con \0char, passa il comando e infine fa la stessa cosa al contrario.

28-08-2015 : ho trovato un altro uso personale per questo strumento: selezionare N paragrafi dopo una riga.

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

foo
bar
baz

a couple
more of these

4

Se hai GNU awk disponibile, puoi ordinare ogni blocco usando la asort()funzione integrata. Qualcosa come questo:

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) }

Eseguilo in questo modo:

awk -f blocksort.awk infile

1

TXR Lisp passo dopo passo:

$ 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

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


Si noti che in the [mapcar sort ...]potremmo sostituire sortcon una funzione che convoglia le stringhe attraverso un processo esterno. Possiamo quindi finire con uno strumento per distribuire un comando esterno di elaborazione del testo su paragrafi.
Kaz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.