Trier les fichiers texte avec plusieurs lignes en ligne

13

J'ai un fichier texte dans ce format:

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

Je veux trier ce fichier par KEYligne et garder les 4 lignes suivantes avec lui dans le résultat, donc le résultat trié devrait être:

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

Y a-t-il un moyen de faire cela ?

RYN
la source
5
ne pas traverser la poste s'il vous plaît
Zanna
@Zanna: Je pense qu'il y a une exclusion pour les sections Unix et Askubuntu car ces deux-là se chevauchent beaucoup! Je pense que j'ai lu à ce sujet dans la section méta d'Unix
RYN
2
méta-question pertinente posée ici par AU mod :) Comment les questions transposées sur Ask Ubuntu devraient-elles être traitées?
Zanna
@RYN Le problème ne serait pas le chevauchement, en fait beaucoup de sites SE se chevauchent, mais le fait que les personnes donnant des réponses ne connaissent peut-être pas les réponses sur l'autre site.
phk

Réponses:

12

msort(1)a été conçu pour pouvoir trier des fichiers avec des enregistrements sur plusieurs lignes. Il a une interface graphique en option, ainsi qu'une version de ligne de commande normale et utilisable pour les humains. (Au moins, les humains qui aiment lire attentivement les manuels et chercher des exemples ...)

AFAICT, vous ne pouvez pas utiliser un modèle arbitraire pour les enregistrements, donc à moins que vos enregistrements soient de taille fixe (en octets, pas en caractères ou en lignes). msorta une -boption pour les enregistrements qui sont des blocs de lignes séparés par des lignes vides.

Vous pouvez transformer votre entrée en un format qui fonctionnera -bassez facilement, en mettant une ligne vierge avant chaque ###...(sauf la première).

Par défaut, il imprime des statistiques sur stderr, donc au moins il est facile de savoir quand il n'a pas été trié car il pensait que l'entrée entière était un seul enregistrement.


msorttravaille sur vos données. La sedcommande ajoute une nouvelle ligne à chaque #+ligne à l'exception de la ligne 1. -wtrie l'enregistrement entier (lexicographiquement). Il existe des options pour choisir la partie d'un enregistrement à utiliser comme clé, mais je n'en avais pas besoin.

J'ai également laissé de côté les nouvelles lignes supplémentaires.

$ 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

Je n'ai pas eu de chance -r '#'de l'utiliser comme séparateur d'enregistrements. Il pensait que le dossier entier était un enregistrement.

Peter Cordes
la source
Merci beaucoup; msortest très utile; merci (à -rce sujet, il semble que ce soit parce qu'il y a plus d'un # que j'ai utilisé -det que cela a fonctionné
RYN
cool! (+1) msort -qwr '#' ex fonctionne pour moi (eh bien, il modifie le séparateur d'enregistrement de sortie)
JJoao
8

Une solution consiste à remplacer d'abord les sauts de ligne à l'intérieur d'un bloc par un caractère inutilisé de votre choix ('|' dans l'exemple ci-dessous), à trier le résultat et à ramener le séparateur choisi au saut de ligne d'origine:

sed -e 'N; N; N; N; N; s/\n/|/g' file.txt \
| sort -k2,2 -t\| \
| sed 's/|/\n/g'
xhienne
la source
1
Merci; cela fonctionne mais c'est très sale surtout quand les données sont sales aussi! si les lignes après la clé étaient 100 alors je dois y mettre 100 ;N, et il peut être difficile de trouver un caractère qui n'est pas utilisé dans le texte lui-même; c'est très bon pour sortou awk, ... pouvoir faire un tri multiligne
RYN
4
perl -0ne 'print sort /(#+[^#]*)/g' file.txt
  • perl -0 avale tout le fichier
  • /(....)/g faire correspondre et extraire les enregistrements
  • print sort ... les trier et les imprimer
JJoao
la source
2

Voici une autre méthode qui devrait fonctionner avec n'importe quel nombre de lignes dans une KEYsection:

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

Cela fonctionne en enregistrant le délimiteur dans une variable (pour ensuite le supprimer de l'entrée). Il ajoute ensuite le KEY*à chaque ligne dans sa section correspondante en utilisant un caractère ascii faible (qui est peu susceptible de se produire dans votre entrée) comme séparateur, puis ninsère toutes les lines en utilisant le même séparateur. Il ne s'agit alors que de sortpasser par les 3ème et 1er champs et de cutting la colonne du milieu puis de restaurer les délimiteurs via une finale sed. Notez qu'avec ce qui précède, KEY12sera trié avant KEY2afin d'ajuster la sortcommande selon vos besoins.

don_crissti
la source
2

Vous pouvez utiliser la bibliothèque stdlib 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]
}
Steven Penny
la source