Ordina i file di testo con più righe come una riga


13

Ho un file di testo in questo formato:

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

Voglio ordinare questo file per KEYriga e mantenere le successive 4 righe con esso in modo che il risultato ordinato dovrebbe essere:

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

C'è un modo per fare questo ?



@Zanna: Penso che ci sia un'esclusione per le sezioni unix e askubuntu in quanto questi due si sovrappongono molto! Penso di averlo letto nella meta sezione di unix
RYN

2
meta domanda pertinente posta qui da AU mod :) Come devono essere gestite le domande post-cross su Ask Ubuntu?
Zanna,

@RYN Il problema non sarebbe la sovrapposizione, in effetti molti siti SE si sovrappongono, ma le persone che danno risposte potrebbero non essere a conoscenza delle risposte sull'altro sito.
phk,

Risposte:


12

msort(1)è stato progettato per essere in grado di ordinare i file con record multilinea. Ha una GUI opzionale, così come una versione normale e utilizzabile dalla linea di comando per gli umani. (Almeno, agli umani a cui piace leggere attentamente i manuali e cercare esempi ...)

AFAICT, non è possibile utilizzare un modello arbitrario per i record, quindi a meno che i record non abbiano dimensioni fisse (in byte, non caratteri o righe). msortha -bun'opzione per i record che sono blocchi di linee separate da righe vuote.

Puoi trasformare il tuo input in un formato che funzionerà -babbastanza facilmente, mettendo una riga vuota prima di ogni ###...(tranne il primo).

Per impostazione predefinita, stampa le statistiche su stderr, quindi almeno è facile dire quando non è stato ordinato perché pensava che l'intero input fosse un singolo record.


msortfunziona sui tuoi dati. Il sedcomando antepone una nuova riga a ogni #+riga tranne la riga 1. -wordina l'intero record (lessicograficamente). Ci sono opzioni per scegliere quale parte di un record usare come chiave, ma non ne avevo bisogno.

Ho anche lasciato fuori lo stripping delle nuove linee extra.

$ 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

Non ho avuto fortuna -r '#'a usarlo come separatore di dischi. Pensava che l'intero file fosse un record.


Grazie mille; msortè molto utile; grazie (a -rquanto pare è perché ci sono più di un # che ho usato -de ha funzionato
RYN

freddo! (+1) msort -qwr '#' ex funziona per me (beh, insegue il separatore del registro delle uscite)
JJoao

8

Una soluzione è innanzitutto cambiare i feed di riga all'interno di un blocco in un carattere non utilizzato di tua scelta ('|' nell'esempio seguente), per ordinare il risultato e ripristinare il separatore scelto nel feed di riga originale:

sed -e 'N; N; N; N; N; s/\n/|/g' file.txt \
| sort -k2,2 -t\| \
| sed 's/|/\n/g'

1
Grazie; funziona ma è molto sporco specialmente quando anche i dati sono sporchi! se le righe dopo la chiave erano 100, allora devo inserire 100 ;Nlì, e può essere difficile trovare un carattere che non è usato nel testo stesso; è molto buono per sorto awk, ... essere in grado di fare l'ordinamento multilinea
RYN

4
perl -0ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0 assorbe l'intero file
  • /(....)/g abbinare ed estrarre i record
  • print sort ... ordinare e stamparli

2

Ecco un altro modo che dovrebbe funzionare con qualsiasi numero di righe in una KEYsezione:

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

Funziona salvando il delimitatore in una variabile (per rimuoverlo dall'input). Quindi aggiunge KEY*a ciascuna riga della sezione corrispondente usando un carattere ASCII basso (che è improbabile che si verifichi nel tuo input) come separatore e quindi nsminuisce tutti gli lines usando lo stesso separatore. È quindi solo una questione di sorting dal 3 ° e 1 ° campo e cutting la colonna centrale e quindi ripristinare i delimitatori tramite un finale sed. Si noti che con quanto sopra, KEY12verranno ordinati prima KEY2quindi regolare il sortcomando in base alle proprie esigenze.


2

È possibile utilizzare la libreria stdlib di POSIX Awk :

#!/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]
}
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.