Ich habe eine große gzip'd Textdatei. Ich möchte etwas wie:
zcat BIGFILE.GZ | \
awk (snag 10,000 lines and redirect to...) | \
gzip -9 smallerPartFile.gz
Der awk-Teil dort oben soll im Grunde genommen 10.000 Zeilen dauern und an gzip senden und dann wiederholen, bis alle Zeilen in der ursprünglichen Eingabedatei verbraucht sind. Ich habe ein Skript gefunden, das behauptet, dies zu tun, aber wenn ich es auf meinen Dateien ausführe und dann das Original von denjenigen unterscheide, die geteilt und dann zusammengeführt wurden, fehlen Zeilen. Also stimmt etwas mit dem awk-Teil nicht und ich bin mir nicht sicher, welcher Teil kaputt ist.
Das Ziel:
- Lesen Sie die Quelldatei einmal für den gesamten Vorgang durch
- Teilen Sie die Quelle in kleinere Teile auf, die durch Zeilenumbrüche begrenzt sind. Angenommen, 10.000 Zeilen pro Datei
- Komprimieren Sie die Zieldateien, die als Ergebnis der Aufteilungsaktion erstellt wurden, und tun Sie dies ohne einen zusätzlichen Schritt, nachdem dieses Skript verarbeitet wurde.
Hier ist der Code. Kann mir jemand sagen, warum dies keine Datei ergibt, die geteilt und zusammengeführt und dann erfolgreich zum Original verschoben werden kann?
# Generate files part0.dat.gz, part1.dat.gz, etc.
# restore with: zcat foo* | gzip -9 > restoredFoo.sql.gz (or something like that)
prefix="foo"
count=0
suffix=".sql"
lines=10000 # Split every 10000 line.
zcat /home/foo/foo.sql.gz |
while true; do
partname=${prefix}${count}${suffix}
# Use awk to read the required number of lines from the input stream.
awk -v lines=${lines} 'NR <= lines {print} NR == lines {exit}' >${partname}
if [[ -s ${partname} ]]; then
# Compress this part file.
gzip -9 ${partname}
(( ++count ))
else
# Last file generated is empty, delete it.
rm -f ${partname}
break
fi
done
close("gzip -9 > " file ".gz")
nicht nurclose(file)
. Ansonsten hat awk keine Ahnung, was zu schließen ist.close(file)
war falsch. Die Wahrscheinlichkeit, dass dies ein Problem darstellt, hängt von der Dateigröße, der Anzahl der verfügbaren Dateideskriptoren und der Anzahl der Zeilen in jeder Datei ab. Es ist sauberer, jede Datei zu schließen, wenn wir damit fertig sind.Die kürzere (und nützlichere) Antwort: Haben Sie sich den Unix-
split
Befehl angesehen?quelle
split
, das wartet, bisfooab
es erstellt wird, und dann zipptfooaa
, dann wartet, bisfooac
es erstellt wird, und dann zipptfooab
und so weiter. Aber das ist ein Kluge und garantiert nicht zu funktionieren.split
. … Aber warten Sie - bedeutet das, dass Sie erwarten, jede Datei in Millionen von Teilen zu zerlegen (400G ÷ 80 = 5000000)? Das könnte ausschließensplit
- ich weiß nicht, ob es mehr als 676 (26²) Ausgabedateien verarbeiten kann.Die kurze Antwort lautet, dass
awk
die Eingabe (zcat
in diesem Fall die Pipe von ) blockweise gelesen wird (wobei ein Block 512 Byte oder ein Vielfaches davon ist, abhängig von Ihrem Betriebssystem). Wenn sich also das 10000. Zeilenumbruchzeichen (Zeilenende-Marker) im Speicher befindet, befindet sich auch die 10001. Zeile, die 10002. und höchstwahrscheinlich auch mehr (oder möglicherweise weniger) im Speicher. Dies ist ein Problem, da diese Zeichen aus der Pipe ausgelesen wurden und nicht mehr für die nächste Iterationawk
zum Lesen verfügbar sind .quelle
Ich habe darüber nachgedacht und einen Weg gefunden, der überhaupt nicht effizient ist und bei dem jede Datei nutzlos vollständig dekomprimiert wird, um jedes Stück aufzunehmen. Wenn Sie also in 20 Teile teilen möchten, werden die großen Dateien 20 Mal dekomprimiert. Es wird jedoch nicht die gesamte Datei gespeichert, sondern nur das komprimierte Teil. Obwohl es speichereffizient ist, ist die CPU ineffizient.
Das Skript sollte mit dem ersten Argument der großen gzip-Datei und dem zweiten Argument der Anzahl der zu teilenden Zeilen ausgeführt werden.
Dadurch wird im selben Verzeichnis für jedes Stück eine Datei mit dem Namen gzip-Datei erstellt und ".lines- $ startline- $ endline.gz" angehängt.
Hoffe du bist in Ordnung CPU zu verschwenden :)
quelle
Sie haben eine schlechte Alternative. Hier erfahren Sie, wie Sie dies mit GNU Split oder GNU Parallel tun können.
GNU Split hat eine
--filter
Option und etwas, das dem sehr nahe kommt, was Sie versuchen, wird im Handbuch beschrieben:In Ihrem Fall könnten Sie also Folgendes tun:
Eine gute Alternative zum Teilen wäre die Verwendung von GNU parallel. Auf diese Weise können Sie die Komprimierung parallelisieren:
quelle