„Multipass“-Skriptänderung großer Dateien direkt vor Ort (auf Dateisystemebene)?

„Multipass“-Skriptänderung großer Dateien direkt vor Ort (auf Dateisystemebene)?

Ich stehe gerade vor dem Problem, einige Zeilen aus einer großen (Gigabyte) Datei ausschneiden zu müssen. Da ich mir der potenziellen CPU-Belastung beim Lesen der Datei im Speicher bewusst bin, wollte ich sie stattdessen direkt bearbeiten ... und bin dabei auf diese Fragen gestoßen:

... und weiters auch diese:

Ich habe mich jedoch über etwas anderes gewundert: Ich glaube (bin mir aber nicht sicher), dass jedes Dateisystem (wie ext3) so etwas wie eine verknüpfte Liste verwenden müsste, um so etwas wie Fragmente einer Datei beschreiben zu können, die auf Bereiche der Festplatte abgebildet sind.

Daher sollte es möglich sein, so etwas zu tun. Nehmen wir zum Beispiel an, ich habe eine Datei bigfile.datwie diese (die Zahlen sollten den Byte-Offset angeben, aber es ist ein bisschen schwierig, sie auszurichten):

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

L 1\n L 2\n L 3\n L 4\n L 5\n L 6\n

Diese Datei könnte dann im Prinzip in eine Terminalanwendung zum Durchsuchen geladen werden - nehmen wir an, wir rufen ein Tool auf editsegments bigfile.datund sagen, es less -N bigfile.datwürde dieselbe Datei (mit Zeilennummern) ähnlich wie folgt anzeigen:

      1      1      L 1
      2      2      L 2 *
      3      3      L 3
      4      4      L 4 *
      5      5      L 5
      6      6      L 6
bigfile.dat (END) 

Ich könnte dort beispielsweise einen Befehl eingeben (etwa dzum Löschen von Zeilen), eine andere Taste oder die Maus dort klicken, wo es oben mit - steht, und *damit sagen, dass alles zwischen den Zeilen 2 und 4 gelöscht werden soll. Das Programm würde dann antworten und folgendes anzeigen:

      1      1      L 1
      2      5      L 5
      3      6      L 6
bigfile.dat (END) 

Jetzt können wir sehen, dass die erste Spalte ganz links die „neue“ Zeilennummer (nach dem Schnitt) zeigt, die zweite Spalte die „alte“ Zeilennummer (vor dem Schnitt) – und dann folgt der eigentliche Zeileninhalt.

Ich stelle mir vor, dass nach editsegmentsdem Beenden dieser Pseudoanwendung zunächst alles bigfile.datunverändert bleibt. Allerdings würde sich jetzt im selben Verzeichnis auch eine zusätzliche Textdatei befinden, beispielsweise bigfile.dat.segmentsmit folgendem Inhalt:

d 4:15 # line 2-4

... und zusätzlich würde eine spezielle Datei (wie ein „Symlink“) – nennen wir sie bigfile.dat.iedit– erscheinen.

Das Ergebnis von all dem wäre im Grunde, dass ich, wenn ich jetzt versuche, bigfile.dat.ieditmit etwas wie zu öffnen less -N bigfile.dat.iedit, den „bearbeiteten“ Inhalt erhalten möchte:

      1 L 1
      2 L 5
      3 L 6
bigfile.dat (END) 

... was, denke ich, dadurch erreicht werden könnte, dass man dem Betriebssystem irgendwie die Anweisung gibt, dass es beim $FILE.ieditÖffnen zuerst $FILE.segmentsöffnen und lesen soll; das d 4:15würde dann anweisen, dass die Bytes 4 bis 15 in der Originaldatei ausgelassen werden sollen - was zu etwa folgendem Ergebnis führen würde:

0 1 2 3 4 5 6 7 8 9 10 11 12,3,4 15 16 17 18 19 20 21 22 23

L 1\n L2\n L3\n L4\n L 5\n L 6\n

0 1 2 3 ------------------------------->16 17 18 19 20 21 22 23

Mit anderen Worten -vorausgesetztdass im Dateisystemkonzept einer Datei jedes Inhaltsbyte auch einen „Link“ zum nächsten Byte in der Kette enthält – es sollte möglich sein, das Dateisystem anzuweisen, auf der Grundlage eines Skripts eine neue verknüpfte Liste zu erstellen und den Inhalt, wie er durch diese geänderte verknüpfte Liste dargestellt wird, über eine spezielle Datei (Symlink oder Pipe) bereitzustellen.

Das ist, was ich mit „geskriptet“ im Titel meinte – dass die „neue“ verknüpfte Liste durch eine Skriptdatei ( $FILE.segments) gesteuert werden kann, die vom Benutzer in einem Texteditor bearbeitet werden kann (oder von einer Frontend-Anwendung generiert wird). Was ich mit „Multipass“ meinte, ist die Tatsache, dass bigfile.datin diesem Prozess überhaupt nichts geändert wird; ich könnte also heute das erste (ursprüngliche) Gigabyte bearbeiten und den Fortschritt in () speichern $FILE.segments– dann könnte ich morgen das zweite Gigabyte bearbeiten und den Fortschritt wieder in ( $FILE.segments) speichern usw. – währenddessen bigfile.datbleibt das Original unverändert.

Wenn alle Änderungen abgeschlossen sind, könnte man wahrscheinlich eine Art Befehl aufrufen (z. B. editsegments --finalize bigfile.dat), der die neue verknüpfte Liste einfach dauerhaft als Inhalt von kodiert bigfile.dat(und entsprechend bigfile.dat.segmentsund entfernt bigfile.dat.iedit). Oder noch einfacher: Man könnte einfach Folgendes tun:

cp bigfile.dat.iedit /path/to/somewhere/else/bigfile.modified.dat

Natürlich dkönnte man neben einem Elete-Skriptbefehl rauch einen Eplace-Befehl haben, etwa:

r 16:18 AAA 

... mit der Aussage: Ersetzen Sie den Inhalt zwischen den Bytes 16 und 18 durch die nächsten 18-16+1=3 Bytes nach dem Leerzeichen (also dem AAA) – die verknüpfte Liste könnte sich tatsächlich in den Inhalt des Skriptbefehls selbst „einhaken“ (die folgende Tabelle enthält auch das dElete):

0 1 2 3 4 5 6 7 8 9 10 11 12,3,4 15 16 17 18 19 20 21 22 23

L 1\n L2\n L3\n L4\n L 5\n L 6\n

0 1 2 3 ----------------------------->| | 19 20 21 22 23

. . ...\n r1  6  :18  AAA \n  . .  . .


Nun, ich vermute, dass Programme wie hexedit(wie erwähntHier) ändern Dateien direkt vor Ort – aber ich möchte den Vorteil der Möglichkeit der Skripterstellung (noch besser, wenn dies über eine GUI-Anwendung geregelt werden könnte, selbst wenn es sich um eine Terminalanwendung handelt) und den Vorteil, dass die Originaldatei nicht tatsächlich geändert wird, bis bestätigt wurde, dass alle Änderungen wie erforderlich sind.

Ich bin nicht sicher, ob so etwas überhaupt möglich ist – und selbst wenn, vermute ich, dass dafür ein dedizierter Treiber erforderlich ist (und nicht nur ein Benutzerprogramm) … Aber ich denke, die Frage ist es trotzdem wert: Gibt es so etwas für Linux?

Vielen Dank im Voraus für alle Antworten,
Prost!

Antwort1

Die Struktur der Dateien auf der Festplatte hängt vom verwendeten Dateisystem ab. Keines der realen Dateisysteme verwendet verknüpfte Listen, wie Sie es beschreiben (das wäre fseek(3)unerträglich). Am nächsten kommt dem MicrosoftsFETT, wodurch die Zeiger im Wesentlichen aus den Datenblöcken in ein Array verschoben werden, das sie überschattet.

Die meisten Dateisysteme verwenden jedoch einige zeigerbasierte Verweise auf Datenblöcke in der Datei. Im Prinzip könnte man also einen Block aus einer Datei ausschneiden, indem man einfach eine Handvoll Zeiger (nicht den gesamten Dateiinhalt) verschiebt und einen Block in der Mitte der Datei als frei markiert. Leider ist dies keine sehr nützliche Operation, da Dateiblöcke ziemlich groß sind (normalerweise 4 KB) und sich selten vernünftig an den Strukturen in der Datei ausrichten (seien es Zeilen oder andere Unterteilungen).

Antwort2

Was Sie beschreiben, klingt sehr nach einemWiederholungeines TexteditorsWiederherstellen-Listemit der unveränderten Originaldatei, zu der dasWiederherstellen-Listegehört. Ich bin ziemlich sicher, dass das gvimso einhartnäckigUndo/Redo-Liste, die Sie vielleicht(?) nutzen können, und ich weiß, dass emacses definitiv eine solche Liste gibt, die Sie höchstwahrscheinlich dazu bringen könnten, alles zu tun, was Sie wollen (über ein elispSkript), z. B.Speichern Sie den Emacs-Rückgängig-Verlauf zwischen den Sitzungen.

Als Randbemerkung: Bei so großen Dateien kann es sinnvoll sein, alle unerwünschten Aktionen zu deaktivieren, z. B.:Automatisches Speichern,Syntaxhervorhebung(langsam auf einemgroßemacs-Datei), usw. und emacs auf einem 32-Bit-System hat eine 256 MBDateigrößenbeschränkung.

Es wird sicherlich nicht so prägnant sein wie Ihr Vorschlag, könnte aber brauchbar sein, wenn es nicht zu viele Änderungen gibt.

Antwort3

Im Allgemeinen können Sie eine Datei nicht direkt bearbeiten, ohne die gesamte Datei in den Speicher zu laden. Ich gehe davon aus, dass Sie eigentlich nur eine neue Datei haben möchten, die eine Kopie der alten ohne bestimmte Zeilen ist. Dies lässt sich leicht mit den Unix-Dienstprogrammen headund erreichen tail. Um beispielsweise alles außer den Zeilen 5, 12 und 52 aus einer Datei zu kopieren, können Sie Folgendes tun:

head -n 4 bigfile.dat > tempfile.dat
tail -n +6 bigfile.dat | head -n 6 >> tempfile.dat 
tail -n +13 bigfile.dat | head -n 39 >> tempfile.dat 
tail -n 53 bigfile.dat >> tempfile.dat

Falls Sie mit diesen Dienstprogrammen nicht vertraut sind, werde ich sie genauer erklären.

Das headDienstprogramm druckt die ersten n Zeilen einer Datei aus. Wenn kein Positionsargument angegeben wird, wird die Standardeingabe als Datei verwendet. Das -nFlag teilt head mit, wie viele Zeilen ausgedruckt werden sollen. head -n 2Es werden also nur die ersten 2 Zeilen der Standardeingabe ausgedruckt.

Das tailDienstprogramm druckt die letzten n Zeilen einer Datei aus. Wie head kann es aus einer Datei oder der Standardeingabe lesen. Das Flag -n teilt tail mit, wie viele Zeilen vom Ende aus ausgedruckt werden sollen. Sie können der Zahl auch ein Pluszeichen voranstellen, um tail anzuweisen, die Zeilen vom Ende der Datei auszugeben, beginnend mit so vielen Zeilen vom Anfang. tail -n 2Druckt beispielsweise die letzten beiden Zeilen der Standardeingabe aus. tail -n +2Druckt jedoch alle Zeilen ab Zeile 2 aus (Zeile 1 wird weggelassen).

Wenn Sie also im Allgemeinen Zeilen im Bereich [x, y) aus einer Datei drucken möchten, tun Sie dies

`tail -n +x | head -n d`

wobei d = y - x. Diese Befehle erzeugen eine neue Datei. Sie können die alte Datei dann löschen, wenn Sie möchten. Der Vorteil dieser Vorgehensweise besteht darin, dass Sie immer headnur taileine Zeile im Speicher behalten müssen, sodass Ihr RAM nicht schnell voll wird.

Antwort4

Klingt nach einem Job für ein Sed-Skript. Wenn ich mich richtig erinnere, wurde es für solche Aufgaben entwickelt. Zeilenweise Verarbeitung, wiederholte Verarbeitung derselben Befehlsgruppe und Regex – alles in einem Tool vereint. Obwohl ich weiß, dass es den Job erledigen wird, kann ich Ihnen nicht weiterhelfen, außer Sie auf die entsprechenden Anweisungen hinzuweisen.manpage.

verwandte Informationen