Aus der Datei extrahieren und neu anordnen

Aus der Datei extrahieren und neu anordnen

Ich habe eine Datei, die ich extrahieren und bestimmte Daten neu anordnen möchte. Die alte Datei enthält Rohdaten. Diese Datei ist eine Eingabe

Referenz:cve,2017-8962
Seite:45885
Referenz:cve,2016-10033
Referenz:cve,2016-10034
Referenz:cve,2016-10045
Referenz:cve,2016-10074
Seite:45917
Referenz:cve,2017-8046
Seite:45976
Referenz:cve,2018-6577
Referenz:cve,2018-6578
Seite:46062

und die folgende Datei ist die neue Datei und enthält die erforderliche Ausgabe

Referenz:cve,2017-8962
Seite:45885
Referenz:cve,2016-10033
Seite:45917
Referenz:cve,2016-10034
Seite:45917
Referenz:cve,2016-10045
Seite:45917
Referenz:cve,2016-10074
Seite:45917
Referenz:cve,2017-8046
Seite:45976
Referenz:cve,2018-6577
Seite:46062
Referenz:cve,2018-6578
Seite:46062
.

Erklärung: z. B. sid:45917 gibt es vier Referenzen (Referenz: CVE, 2016-10033 Referenz: CVE, 2016-10034 Referenz: CVE, 2016-10045 Referenz: CVE, 2016-10074). Wir müssen jede Referenz aufteilen und die Sid untereinander anhängen (Hinweis: Auf sid folgt immer eine Referenz), so gibt es sich wiederholende Blöcke, wenn also mehrere Referenzen vorhanden sind, müssen wir sie in der neuen Dateireihenfolge anhängen.

Antwort1

Wie Sie scheinen zu verwendennachgepostet sid:s (Multiple references:gefolgt von ihren einzelnen sids:=> Paare von references:und sid:), zwei Lösungen.


Lösung 1: Rückwärtsfahren

Verwenden Sie einfach den tacBefehl (es istKatzein umgekehrter Reihenfolge), um die Eingabe und die Ausgabe umzukehren:tac input | awk | tac > output

Für den awk-Teil duplizieren Sie einfach das sid:s:

gawk '/^sid:/{sid=$0};/^reference:/{print sid "\n" $0}'

Lösung 2: Array

Speichern Sie die reference:s in einem Array, sobald sie kommen, und geben Sie sie dann wieder aus, wenn Sie auf entsprechendesid:

gawk 'BEGIN{r=0};/^reference:/{ref[r++]=$0};/^sid:/{for(n=0;n<r;n++){print ref[n] "\n" $0};r=0}' /tmp/test.txt

/^reference:/{ref[r++]=$0}: für jede Zeile, die mit „ref…“ beginnt, speichern Sie die Zeile in einem Array und verschieben Sie den Zeiger „r“ zum nächsten Element.

/^sid:/{for(n=0;n<r;n++){print ref[n] "\n" $0};r=0}: Wenn eine Zeile mit „sid“ beginnt, durchlaufen Sie das gesamte Array bis zum Zeiger „r“ (für …) und drucken Sie für jedes Element den gespeicherten Verweis und die aktuelle Zeile (= „sid“). Setzen Sie dann „r“ wieder auf den Anfang zurück, damit wir erneut mit den nächsten Verweisen beginnen.

Antwort2

awk 'BEGIN { i=0; }
/^reference:/ { ref[i++] = $0; }
/^sid:/ { for(j=0; j<i; j++) { print ref[j]; print; } i=0; }' inputfile > outputfile

Erläuterung:

  • BEGIN { i=0; }Initialisieren Sie die Variable, um sicherzustellen, dass sie als numerischer Wert 0und nicht als leere Zeichenfolge interpretiert wird "".
  • /^reference:/ { ref[i++] = $0; }Für jede Zeile, die mit reference:( ^ist ein Anker zum Zeilenanfang) beginnt, kopieren Sie die gesamte Zeile $0in ein Array-Element ref[i]und erhöhen Sie den Indexi++
  • /^sid:/ { ... }für jede Zeile, die mit sid:... beginnt
  • for(j=0; j<i; j++) { ... }As izeigt auf das Array-Element nach dem zuletzt verwendeten, durchlaufen Sie alle Array-Elemente, in die mit dem Index geschrieben wurde j.
  • print ref[j];druckt den Inhalt des Array-Elements, also eine gespeicherte reference:Zeile
  • print;druckt die aktuelle Zeile, also die sid:Zeile
  • i=0;reference:setzt den Array-Index für die nächste Zeilengruppe auf den Anfang zurück

Das Skript basiert auf den folgenden Annahmen:

  • Die Eingabe besteht aus einer Reihe von Blöcken, wobei jeder Block enthält
    • eine Folge von einer oder mehreren reference:Zeilen, gefolgt von
    • eine einzelne sid:Zeile
  • Die letzte Zeile muss eine sid:Linie sein.
  • Nicht übereinstimmende Zeilen werden ignoriert.

Bei der ursprünglichen Frage bin ich von der falschen Richtung der Konvertierung ausgegangen. Das zweite Skript konvertiert in die entgegengesetzte Richtung:

awk 'BEGIN { oldsid=""; ref=""; }
/^reference:/ { ref=$0; }
/^sid:/ { if(oldsid != $0) { if(oldsid != "") print oldsid; } if(ref!="")print ref; oldsid=$0; }
END { if (oldsid != "") print oldsid; }' inputfile > outputfile

Erläuterung:

  • BEGIN { oldsid=""; ref=""; }Initialisieren Sie Variablen der Übersichtlichkeit halber, aber nicht wirklich notwendig.
  • /^reference:/ { ref=$0; }Drucken Sie jede Zeile, die mit „ reference:Zeile $0in Variable speichern“ beginnt ref, noch nicht aus.
  • /^sid:/ { ... }sid:Für jede Zeile, die mit ... beginnt .
  • if(oldsid != $0) { if(oldsid != "") print oldsid; }Wenn sich die sid:Zeile jetzt geändert hat, gehört die letzte reference:in gespeicherte Zeile refzur neuen sid:, daher drucken wir sie noch nicht. Wenn oldsidnicht leer ist, können wir es jetzt drucken, da der vorherige Zeilenblock reference:mit derselben sid:beendet ist. oldsidwird leer sein, wenn wir die erste finden sid:.
  • if(ref!="")print ref;Wenn wir ein gespeichertes haben reference:, drucken wir es jetzt aus. (Entweder haben wir gerade den vorherigen Block mit der entsprechenden sid:Zeile geschlossen oder wir wissen jetzt, dass der aktuelle Block reference:dasselbe hat sid:wie der vorherige.) Die Prüfung auf leere Zeichenfolge ist nicht wirklich erforderlich, da ich davon ausgehe, dass jeder sid:Zeile eine Zeile vorangeht reference:.
  • oldsid=$0;Speichern Sie die aktuelle sid:Zeile für den Vergleich, wenn wir die nächste erhalten. Die aktuelle Zeile wird noch nicht gedruckt.
  • END { if (oldsid != "") print oldsid; }Am Ende wird die letzte gespeicherte sid:Zeile gedruckt, sofern vorhanden. (Wenn die Eingabedatei leer ist, wird hier keine leere Zeile gedruckt.)

Dieses Skript basiert auf diesen Annahmen:

  • Auf jedes reference:folgt einsid:
  • alle Paare von reference:und sid:mit der gleichen sid:Linie folgen aufeinander

verwandte Informationen