¿Clasificación agrupada de párrafos continuos (separados por una línea en blanco)?

8

Creo que ahora tengo bastante experiencia en ordenar por columnas ; Sin embargo, no he encontrado nada hasta ahora sobre cómo ordenar filas continuas .

Supongamos que tenemos un archivo de texto que se ve así: (muy simplificado, por supuesto)

Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

Ahora, ¿es posible ordenar las líneas alfanuméricamente por cada bloque por separado ? Quiero decir, para que el resultado se vea así:

Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Según lo que encontré en la sortpágina del manual, esto podría no ser posible con el sortcomando incorporado de UNIX . ¿O incluso puede hacerse sin tener que recurrir a herramientas externas / de terceros?

error de sintaxis
fuente

Respuestas:

9

La awksolución de Drav es buena, pero eso significa ejecutar un sortcomando por párrafo. Para evitar eso, podrías hacer:

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

O podrías hacer todo en perl:

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

Tenga en cuenta que arriba, los separadores son líneas en blanco (para el awkuno, líneas con solo espacio o caracteres de tabulación, para el perluno, cualquier carácter de espaciado horizontal o vertical) en lugar de líneas vacías. Si desea líneas vacías, puede reemplazar !NFcon !lengtho $0=="", y /\S/con /./.

Stéphane Chazelas
fuente
¡Gracias a ti también, especialmente por la awksolución que evita los sortgastos generales! ¡Furtivo!
syntaxerror
9
awk -v RS= -v cmd=sort '{print | cmd; close(cmd); print ""}' file

Establecer el separador de registros RSen una cadena vacía hace un paso difícil en los párrafos a la vez. Para cada párrafo, canalice el párrafo (in $0) a cmd (que se establece en sort) e imprima la salida. Imprima una línea en blanco para separar los párrafos de salida con a print "".

Si damos ejemplos de Perl, entonces presento un enfoque alternativo al de Stephane:

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

Desarma el separador de campo ( undef $/), esto nos permite usar <>y obtener todo el STDIN. Luego hacemos spliteso \n\n(párrafos). foreach"párrafo", sortlas líneas splitmarcando alrededor de las nuevas líneas, sorting y luego joinvolviendo a juntarlas y añadiendo un final \n.

Sin embargo, esto tiene un efecto secundario al agregar un separador de "párrafo final" en el último párrafo (si no tenía uno antes). Puedes evitar eso con un poco menos bonito:

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

Esto asigna los párrafos a @list, y luego hay una "operación ternaria" para verificar si es el último elemento de foreach(la \$_ == \$list[-1]verificación). print ""si es ( ? ...), else ( : ...) imprime "\n"para todos los demás "párrafos" (elementos de @list).

Drav Sloan
fuente
Esto es genial! Gracias. ¿Realmente estás invocando /usr/bin/sortcon esa línea o es un awkcomando "ordenar" incorporado?
syntaxerror
Invocando el comando de clasificación, de ahí el requisito de cerrar (cmd) en cada ciclo :)
Drav Sloan
5

Escribí una herramienta en haskell que te permite usar sort, shuf, tac o cualquier otro comando en párrafos de texto.

https://gist.github.com/siers/01306a361c22f2de0122
EDITAR: la herramienta también se incluye en este repositorio: https://github.com/siers/haskell-import-sort

Divide el texto en bloques, une los subbloques con \0char, canaliza a través del comando y finalmente hace lo mismo a la inversa.

28-08-2015 : Encontré otro uso personal para esta herramienta: seleccionar N párrafos después de una línea.

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

foo
bar
baz

a couple
more of these
Raitis Veinbahs
fuente
4

Si tiene GNU awk disponible, puede ordenar cada bloque utilizando la asort()función incorporada. Algo como esto:

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

Ejecútelo así:

awk -f blocksort.awk infile
Thor
fuente
1

TXR Lisp paso a paso:

$ 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

Referencias: get-lines , partición * , op , where , chain , length , zerop , mapcar , interpose .

Kaz
fuente
Tenga en cuenta que en el [mapcar sort ...]podríamos reemplazar sortpor una función que canaliza las cadenas a través de un proceso externo. Entonces podemos terminar con una herramienta para distribuir un comando externo de procesamiento de texto sobre párrafos.
Kaz