
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 tac
Befehl (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 Wert0
und nicht als leere Zeichenfolge interpretiert wird""
./^reference:/ { ref[i++] = $0; }
Für jede Zeile, die mitreference:
(^
ist ein Anker zum Zeilenanfang) beginnt, kopieren Sie die gesamte Zeile$0
in ein Array-Elementref[i]
und erhöhen Sie den Indexi++
/^sid:/ { ... }
für jede Zeile, die mitsid:
... beginntfor(j=0; j<i; j++) { ... }
Asi
zeigt auf das Array-Element nach dem zuletzt verwendeten, durchlaufen Sie alle Array-Elemente, in die mit dem Index geschrieben wurdej
.print ref[j];
druckt den Inhalt des Array-Elements, also eine gespeichertereference:
Zeileprint;
druckt die aktuelle Zeile, also diesid:
Zeilei=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
- eine Folge von einer oder mehreren
- 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$0
in Variable speichern“ beginntref
, noch nicht aus./^sid:/ { ... }
sid:
Für jede Zeile, die mit ... beginnt .if(oldsid != $0) { if(oldsid != "") print oldsid; }
Wenn sich diesid:
Zeile jetzt geändert hat, gehört die letztereference:
in gespeicherte Zeileref
zur neuensid:
, daher drucken wir sie noch nicht. Wennoldsid
nicht leer ist, können wir es jetzt drucken, da der vorherige Zeilenblockreference:
mit derselbensid:
beendet ist.oldsid
wird leer sein, wenn wir die erste findensid:
.if(ref!="")print ref;
Wenn wir ein gespeichertes habenreference:
, drucken wir es jetzt aus. (Entweder haben wir gerade den vorherigen Block mit der entsprechendensid:
Zeile geschlossen oder wir wissen jetzt, dass der aktuelle Blockreference:
dasselbe hatsid:
wie der vorherige.) Die Prüfung auf leere Zeichenfolge ist nicht wirklich erforderlich, da ich davon ausgehe, dass jedersid:
Zeile eine Zeile vorangehtreference:
.oldsid=$0;
Speichern Sie die aktuellesid:
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 gespeichertesid:
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:
undsid:
mit der gleichensid:
Linie folgen aufeinander