Effizientes Entfernen von In-Place-Headern für große Dateien mit sed?

24

Die folgenden Befehle können je nach Dateigröße einige Minuten dauern. Gibt es eine effizientere Methode?

sed -i 1d large_file 
Cheng
quelle

Antworten:

34

Versuchen Sie edstattdessen:

ed <<< $'1d\nwq' large_file

Wenn dieses "große" etwa 10 Millionen Zeilen oder mehr bedeutet, sollten Sie es besser verwenden tail. Ist nicht in der Lage, direkt zu bearbeiten, aber seine Leistung macht dies unverzeihlich:

tail -n +2 large_file > large_file.new

Bearbeiten , um einige Zeitunterschiede anzuzeigen :

( awkCode von Jaypal hinzugefügt, um Ausführungszeiten auf demselben Computer zu haben (CPU 2.2GHz).)

bash-4.2$ seq 1000000 > bigfile.txt # further file creations skipped

bash-4.2$ time sed -i 1d bigfile.txt
time 0m4.318s

bash-4.2$ time ed -s <<< $'1d\nwq' bigfile.txt
time 0m0.533s

bash-4.2$ time perl -pi -e 'undef$_ if$.==1' bigfile.txt
time 0m0.626s

bash-4.2$ time { tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt; }
time 0m0.034s

bash-4.2$ time { awk 'NR>1 {print}' bigfile.txt > newfile.txt && mv -f newfile.txt bigfile.txt; }
time 0m0.328s
Mann bei der Arbeit
quelle
Im Fall tailwürde zähle ich eher die Zeit zu tun , sowohl die erste Zeile zu entfernen und ersetzen bigfile.txtmit bigfile.new.
rozcietrzewiacz
@rozcietrzewiacz, dein Punkt ist richtig. Vielen Dank. Aktualisiert.
Manatwork
Das ist wirklich cool! Ich tat das gleiche mit awkund bekam das folgende Ergebnis -[jaypal:~/Temp] seq 1000000 > bigfile.txt [jaypal:~/Temp] time awk 'NR>1 {print}' bigfile.txt >newfile.txt real 0m0.649s user 0m0.601s sys 0m0.033s
Jaypal Singh
1
@ Jaypal, ich habe deinen Code zur Liste der Alternativen hinzugefügt. Auf meiner Maschine war es noch schneller. Seltsamerweise erwartete ich, dass awkdie Leistung näher an sedder von liegt. (Anmerkung für mich: Niemals erwarten - stattdessen testen.)
Manatwork
In meinem Fall war dies die beste Lösung: tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt;Ich verwende eine einzelne Datei mit einer Sperre, um eine einzelne Aufgabenliste zu verfolgen, die von mehreren Prozessen verwendet wird. Ich begann mit dem, was die ursprüngliche Plakat verwendet: sed -i 1d large_file . Dadurch wurde die Datei für 1-2 Sekunden gesperrt. Die tail/mvKombo wird fast augenblicklich beendet. Vielen Dank!
Chris Adams
6

Es gibt keine Möglichkeit, Dinge effizient vom Anfang einer Datei zu entfernen. Um Daten von Anfang an zu entfernen, muss die gesamte Datei neu geschrieben werden.

Das Abschneiden vom Ende einer Datei kann jedoch sehr schnell gehen (das Betriebssystem muss nur die Dateigrößeninformationen anpassen und möglicherweise nicht verwendete Blöcke löschen). Dies ist im Allgemeinen nicht möglich, wenn Sie versuchen, eine Datei aus dem Kopf zu entfernen.

Es könnte theoretisch "schnell" sein, wenn Sie einen ganzen Block / eine ganze Ausdehnung genau entfernt haben, aber dafür gibt es keine Systemaufrufe, sodass Sie sich auf die dateisystemspezifische Semantik verlassen müssten (falls vorhanden). (Oder ich habe eine Art Offset innerhalb des ersten Blocks / der ersten Ausdehnung, um den tatsächlichen Dateianfang zu markieren. Ich habe auch noch nie davon gehört.)

Matte
quelle
Wenn die Datei sehr groß ist, ist der E / A-Overhead wahrscheinlich (möglicherweise) größer als der CPU-Overhead, der zum Verarbeiten des Zeilenendes erforderlich ist.
Mat
Du hast recht. Es kann jedoch Unterschiede in der Art und Weise geben, wie die Tools auf den Dateiinhalt zugreifen. Das Beste ist, nicht Zeile für Zeile zu verarbeiten, wenn dies nicht erforderlich ist, oder zumindest nicht Zeile für Zeile zu lesen, wenn dies nicht erforderlich ist.
Manatwork
2
Ich bin überrascht, dass der Unterschied in Ihren Ergebnissen so groß ist, und kann ihn mit dieser Dateigröße hier reproduzieren. Die Vorteile scheinen mit zunehmender Dateigröße abzunehmen (probiert mit 10M, 15s für sed, 5s für ed). Trotzdem gute Tipps (+1).
Mat
Ab Version 3.15, Linux verfügt nun über eine API kollabieren Teile einer Datei auf einem gewissen Grad basierte Dateisysteme, aber zumindest für ext4 , die nur auf volle Blöcke durchgeführt werden kann ( in der Regel 4k).
Stéphane Chazelas
Auch wenn für die Bearbeitung die gesamte Datei neu geschrieben werden muss, ist es manchmal sehr praktisch, Befehlszeilentools zum effizienten Bearbeiten zu haben. In meinem Fall hat dies geholfen, als ich die erste Zeile einer Datei entfernen musste, die größer war als mein gesamter System-RAM.
Jason
3

Die effizienteste Methode, tu es nicht! In jedem Fall benötigen Sie doppelt so viel Speicherplatz auf der Festplatte und verschwenden E / A-Vorgänge.

Wenn Sie mit einer großen Datei nicht weiterkommen, die Sie ohne die erste Zeile lesen möchten, warten Sie, bis Sie sie zum Entfernen der ersten Zeile lesen müssen. Wenn Sie die Datei von stdin an ein Programm senden müssen, verwenden Sie tail, um dies zu tun:

tail -n +2 | your_program

Wenn Sie die Datei lesen müssen, können Sie die erste Zeile entfernen, jedoch nur, wenn Sie über den erforderlichen Speicherplatz auf der Festplatte verfügen:

tail -n +2 | tee large_file2 | your_program

Wenn Sie nicht von stdin lesen können, verwenden Sie ein Fifo:

mkfifo large_file_wo_1st_line
tail -n +2 large_file > large_file_wo_1st_line&
your_program -i large_file_wo_1st_line

Umso besser, wenn Sie bash verwenden, können Sie die Prozessersetzung nutzen:

your_program -i <(tail -n +2 large_file)

Wenn Sie in der Datei suchen müssen, sehe ich keine bessere Lösung, als nicht an erster Stelle mit der Datei stecken zu bleiben. Wenn diese Datei von stdout generiert wurde:

large_file_generator | tail -n +2 > large_file

Ansonsten gibt es immer die FIFO- oder Prozesssubstitutionslösung:

mkfifo large_file_with_1st_file
large_file_generator -o large_file_with_1st_file&
tail -n +2 large_file_with_1st_file > large_file_wo_1st_file

large_file_generator -o >(tail -n 2+ > large_file_wo_1st_file)
jfg956
quelle
1

Sie können Vim im Ex-Modus verwenden:

ex -sc '1d|x' large_file
  1. 1 erste Zeile auswählen

  2. d löschen

  3. x speichern und schließen

Steven Penny
quelle
0

Das ist nur theoretisch, aber ...

Ein benutzerdefiniertes Dateisystem (implementiert mit FUSE oder einem ähnlichen Mechanismus) kann ein Verzeichnis verfügbar machen, dessen Inhalt genau mit dem eines bereits vorhandenen Verzeichnisses an einer anderen Stelle übereinstimmt, dessen Dateien jedoch nach Belieben gekürzt werden. Das Dateisystem würde alle Datei-Offsets übersetzen. Dann müssten Sie eine Datei nicht zeitaufwändig umschreiben.

Angesichts der Tatsache, dass diese Idee nicht trivial ist, wäre die Implementierung eines solchen Dateisystems zu teuer / zeitaufwendig, um praktisch zu sein, es sei denn, Sie haben Dutzende von Terabytes solcher Dateien.

Liori
quelle