Wie teilt man eine große Datei in zwei Teile, nach einem Muster?
Ein Beispiel gegeben file.txt:
ABC
EFG
XYZ
HIJ
KNL
Ich möchte diese Datei XYZso aufteilen, dass sie file1die Zeilen bis zu XYZund den Rest der Zeilen enthält file2.
Wie teilt man eine große Datei in zwei Teile, nach einem Muster?
Ein Beispiel gegeben file.txt:
ABC
EFG
XYZ
HIJ
KNL
Ich möchte diese Datei XYZso aufteilen, dass sie file1die Zeilen bis zu XYZund den Rest der Zeilen enthält file2.
Antworten:
Mit awkkönnen Sie:
awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile
Erläuterung: Das erste awkArgument ( out=file1) definiert eine Variable mit dem Dateinamen, die für die Ausgabe verwendet wird, während das nachfolgende Argument ( largefile) verarbeitet wird. Das awkProgramm druckt alle Zeilen in die durch die Variable out( {print >out}) angegebene Datei . Wenn das Muster gefunden XYZwird, wird die Ausgabevariable neu definiert, um auf die neue Datei ( {out="file2}") zu verweisen, die als Ziel zum Drucken der nachfolgenden Datenzeilen verwendet wird.
Verweise:
Dies ist ein Job für csplit:
csplit -sf file -n 1 large_file /XYZ/
würde silently die Akte aufteilen und Stücke mit pre verursachenf ix fileund numbered eine einzelne Ziffer, zB unter Verwendung file0usw. Beachten Sie, dass mit /regex/, würde sich trennen , aber nicht einschließlich der Linie , dass Streichhölzer regex. Fügen Sie einen Versatz hinzu, um die Zeilenübereinstimmung aufzuteilen und einzuschließen:regex+1
csplit -sf file -n 1 large_file /XYZ/+1
Dadurch entstehen zwei Dateien, file0und file1. Wenn Sie unbedingt einen Namen benötigen file1und file2dem csplitBefehl immer ein leeres Muster hinzufügen und die erste Datei entfernen möchten:
csplit -sf file -n 1 large_file // /XYZ/+1
schafft file0 , file1und file2doch file0ist leer , so dass Sie es sicher entfernen können:
rm -f file0
Mit einem modernen ksh hier ist eine Shell-Variante (dh ohne sed) einer der sedoben genannten Antworten:
{ read in <##XYZ ; print "$in" ; cat >file2 ;} <largefile >file1
Und noch eine Variante in ksh alleine (also auch weglassen cat):
{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1
(Die reine kshLösung scheint ziemlich performant zu sein; bei einer 2,4-GB-Testdatei dauerte es 19-21 Sekunden, verglichen mit 39-47 Sekunden bei der sed/cat -basierten Ansatz).
readund print- Sie sollten es einfach gehen lassen, um alles selbst auszugeben. Die Leistung wird besser, wenn Sie das AST-Toolkit vollständig ksherstellen und alle eingebauten Komponenten kompilieren - für mich sedist es seltsam, dass dies eigentlich keine davon ist. Aber bei while <file dosed
awkIhre Benchmark entwickelt? Und obwohl ich mir ziemlich sicher bin ksh, dass Sie diesen Kampf wahrscheinlich immer gewinnen werden, sind Sie mit GNU sednicht sehr fair umzugehen sed- GNUs -unbuffered ist ein pissarmer Ansatz für POSIXLY, um sicherzustellen, dass der Offset des Deskriptors dort verbleibt, wo das Programm beendet wird Es sollte keine Notwendigkeit bestehen, den regulären Betrieb des Programms zu verlangsamen. Die Pufferung ist in Ordnung. Alles sed, was Sie tun müssen, ist, den Deskriptor zu suchen, wenn Sie fertig sind. Aus irgendeinem Grund kehrt GNU diese Mentalität um.
while; Das Drucken erfolgt implizit als definierter Nebeneffekt des <##Umleitungsoperators. Und nur die passende Zeile muss gedruckt werden. (Auf diese Weise ist die Implementierung des Shell-Features für die Unterstützung von incl./excl am flexibelsten.) Eine explizite whileSchleife ist vermutlich wesentlich langsamer (wurde jedoch nicht überprüft).
headanstelle der read; es scheint nur ein wenig langsamer, aber es ist knapperer Code: { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3.
Versuchen Sie dies mit GNU sed:
sed -n -e '1,/XYZ/w file1' -e '/XYZ/,${/XYZ/d;w file2' -e '}' large_file
sed -e '1,/XYZ/{w file1' -e 'd}' large_file > file2
Ein einfacher Hack besteht darin, entweder auf STDOUT oder STDERR zu drucken, je nachdem, ob das Zielmuster übereinstimmt. Sie können dann die Umleitungsoperatoren der Shell verwenden , um die Ausgabe entsprechend umzuleiten. Beispiel: In Perl wird angenommen, dass die Eingabedatei aufgerufen wird fund die beiden Ausgabedateien f1und f2:
Verwerfen der Linie, die dem Aufteilungsmuster entspricht:
perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2Einschließlich der übereinstimmenden Zeile:
perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2Alternativ können Sie auch in andere Dateizugriffsnummern drucken:
Verwerfen der Linie, die dem Aufteilungsmuster entspricht:
perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' fEinschließlich der übereinstimmenden Zeile:
perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
$a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
XYZZeile in die Ausgabe einbezogen werden oder nicht?