Ordenar archivos de texto con varias líneas como una fila

13

Tengo un archivo de texto en este formato:

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

Quiero ordenar este archivo por KEYlínea y mantener las siguientes 4 líneas con él en el resultado, por lo que el resultado ordenado debería ser:

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

Hay alguna forma de hacer esto ?

RYN
fuente
55
no cruce la publicación por favor
Zanna
@Zanna: ¡Creo que hay una exclusión para las secciones de Unix y Askubuntu ya que estas dos se superponen mucho entre sí! Creo que leí sobre esto en la sección meta de Unix
RYN
2
meta pregunta relevante hecha aquí por AU mod :) ¿Cómo deben manejarse las preguntas publicadas en Ask Ubuntu?
Zanna
@RYN El problema no sería la superposición, de hecho, muchos sitios SE se superponen, sino que las personas que dan respuestas podrían no saber sobre las respuestas en el otro sitio.
phk

Respuestas:

12

msort(1)fue diseñado para poder ordenar archivos con registros de varias líneas. Tiene una interfaz gráfica de usuario opcional, así como una versión de línea de comando normal y utilizable para humanos. (Al menos, a los humanos que les gusta leer los manuales cuidadosamente y buscar ejemplos ...)

AFAICT, no puede usar un patrón arbitrario para los registros, a menos que sus registros sean de tamaño fijo (en bytes, no en caracteres o líneas). msorttiene una -bopción para registros que son bloques de líneas separadas por líneas en blanco.

Puede transformar su entrada en un formato que funcione con -bbastante facilidad, poniendo una línea en blanco antes de cada ###...(excepto la primera).

Por defecto, imprime estadísticas en stderr, por lo que al menos es fácil saber cuándo no se ordenó porque pensaba que toda la entrada era un solo registro.


msorttrabaja en tus datos. El sedcomando antepone una nueva línea a cada #+línea excepto la línea 1. -wordena todo el registro (lexicográficamente). Hay opciones para elegir qué parte de un registro usar como clave, pero no las necesitaba.

También dejé de lado las nuevas líneas adicionales.

$ 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

No tuve suerte con -r '#'usar eso como separador de registros. Pensó que todo el archivo era un registro.

Peter Cordes
fuente
muchas gracias; msortes muy útil; gracias ( -rparece que es porque hay más de un # que usé -dy funcionó
RYN
¡frio! (+1) msort -qwr '#' ex funciona para mí (bueno, cambia el separador de grabación de salida)
JJoao
8

Una solución es cambiar primero los avances de línea dentro de un bloque a un carácter no utilizado de su elección ('|' en el ejemplo a continuación), ordenar el resultado y volver a cambiar el separador elegido al avance de línea original:

sed -e 'N; N; N; N; N; s/\n/|/g' file.txt \
| sort -k2,2 -t\| \
| sed 's/|/\n/g'
xhienne
fuente
1
Gracias; esto funciona pero está muy sucio, especialmente cuando los datos también están sucios. si las líneas después de la clave eran 100, entonces necesito poner 100 ;Nallí, y puede ser difícil encontrar un carácter que no se use en el texto mismo; es muy bueno para sorto awk... ser capaz de hacer una clasificación multilínea
RYN
4
perl -0ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0 sorbe todo el archivo
  • /(....)/g unir y extraer los registros
  • print sort ... ordenarlos e imprimirlos
JJoao
fuente
2

Aquí hay otra forma que debería funcionar con cualquier número de líneas en una KEYsección:

# 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}'

Esto funciona guardando el delimitador en una variable (para luego eliminarlo de la entrada). Luego agrega el KEY*a cada línea en su sección correspondiente usando un carácter ascii bajo (que es poco probable que ocurra en su entrada) como separador y luego nnumera todas las entradas lusando el mismo separador. Entonces solo es cuestión de sortpasar por el tercer y primer campo y cutmarcar la columna central y luego restaurar los delimitadores a través de una final sed. Tenga en cuenta que con lo anterior, KEY12se ordenará antes, KEY2así que ajuste el sortcomando según sus necesidades.

don_crissti
fuente
2

Puede usar la biblioteca POSIX Awk stdlib :

#!/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
fuente