RE: Etwas an den Anfang einer sehr großen Textdatei einfügen
Oliver, Oliver Block schrieb:
Am Dienstag, 16. Mai 2006 10:37 schrieb GUNREBEN, PETER (Peter):
cat LargeFile.txt | buffer | cat ToPrependFile.txt - > LargeFile.txt
würdest Du die Zeile mal etwas erläutern?
ja klar! Also, 1) "cat LargeFile.txt" liest die Datei LargeFile.txt und schreibt ihren Inhalt in die pipe. 2) Der "buffer" liest die pipe, speichert den Inhalt zwischen und schreibt ihn dann in die nächste pipe. 3) "cat ToPrependFile.txt -" liest erst die Datei ToPrependFile.txt und danach STDIN (wird durch das "-" Zeichen gekennzeichnet) und schreibt beides in dieser Reihenfolge nach STDOUT 4) Schliesslich wird STDOUT über "> LargeFile.txt" wieder in die grosse Datei LargeFile.txt zurückgeschrieben. Gruss, Peter.
GUNREBEN, PETER (Peter), Dienstag, 16. Mai 2006 18:34:
3) "cat ToPrependFile.txt -" liest erst die Datei ToPrependFile.txt und danach STDIN (wird durch das "-" Zeichen gekennzeichnet) und schreibt beides in dieser Reihenfolge nach STDOUT
4) Schliesslich wird STDOUT über "> LargeFile.txt" wieder in die grosse Datei LargeFile.txt zurückgeschrieben.
Wie kann es sein, daß es kein Kuddelmuddel gibt, wenn ich in dieselbe Datei schreibe, während ich sie auslese? -- Andre Tann
Andre Tann wrote:
GUNREBEN, PETER (Peter), Dienstag, 16. Mai 2006 18:34:
3) "cat ToPrependFile.txt -" liest erst die Datei ToPrependFile.txt und danach STDIN (wird durch das "-" Zeichen gekennzeichnet) und schreibt beides in dieser Reihenfolge nach STDOUT
4) Schliesslich wird STDOUT über "> LargeFile.txt" wieder in die grosse Datei LargeFile.txt zurückgeschrieben.
Wie kann es sein, daß es kein Kuddelmuddel gibt, wenn ich in dieselbe Datei schreibe, während ich sie auslese?
Hallo Andre, Du hast Recht, ich halte die Vorgehensweise "cat LargeFile.txt | buffer | cat ToPrependFile.txt - > LargeFile.txt" für ziemlich gewagt!!! Sollte nämlich die Datei ToPrependFile.txt groß genug sein (auf jeden Fall größer als der Buffer), dann dürfte es in der Tat ein ziemliches Kuddel Muddel geben. Falls die Datei (deutlich) kleiner als der Buffer ist, dürfte es WAHRSCHEINLICH klappen, aber verlassen würde ich mich nicht darauf. Also, ich würde so entweder überhaupt nicht oder sehr vorsichtig vorgehen ;-) Gruß Martin
On Tuesday 16 May 2006 18:34, GUNREBEN, PETER (Peter) wrote:
Oliver Block schrieb:
Am Dienstag, 16. Mai 2006 10:37 schrieb GUNREBEN, PETER (Peter):
cat LargeFile.txt | buffer | cat ToPrependFile.txt - > LargeFile.txt
würdest Du die Zeile mal etwas erläutern?
ja klar! Also,
1) "cat LargeFile.txt" liest die Datei LargeFile.txt und schreibt ihren Inhalt in die pipe.
2) Der "buffer" liest die pipe, speichert den Inhalt zwischen und schreibt ihn dann in die nächste pipe.
3) "cat ToPrependFile.txt -" liest erst die Datei ToPrependFile.txt und danach STDIN (wird durch das "-" Zeichen gekennzeichnet) und schreibt beides in dieser Reihenfolge nach STDOUT
4) Schliesslich wird STDOUT über "> LargeFile.txt" wieder in die grosse Datei LargeFile.txt zurückgeschrieben.
Ich habe da so meine Zweifel, dass das funktioniert. Leider habe ich das Wunderprogramm buffer nicht. Daher habe ich es durch einen Perl-Einzeiler ersetzt, der den STDIN vollständig einliest und erst dann anfängt auf STDOUT zu schreiben. Das eignet sich sicher nicht für große Files, ist in diesem Fall aber als Grenzfall des Buffers geeignet. Also ein kleines Experiment und dann einige Erklärungen: 0 r2@opi ~$ cat x xxxx 0 r2@opi ~$ cat y yyyy 0 r2@opi ~$ cat x | perl -e 'undef $/; print <>;' | cat y - >x 0 r2@opi ~$ cat x yyyy 0 r2@opi ~$ cat y yyyy 0 r2@opi ~$ Angenommen x ist das große File, an das am Anfang etwas angehängt (der Inhalt von y) werden soll. Nach dem Experiment enthält x leider nur noch den Inhalt von y. Der eigentliche Inhalt ist verloren, :-( Der Effekt ist erklärbar, wenn man sich die Kommandozeile näher betrachtet: Ein-/Ausgabeumleitungen macht die Shell. D.h. das ">x" am Ende wird von der Shell als erstes durchgeführt. Der ">" Operator überschreibt (truncate) ein File. Damit ist x schon überschrieben/leer, wenn das cat-Kommando am Anfang das File öffnet. Ob ein Buffer-Programm dazwischengeschaltet ist, ist dabei unwichtig. Nun eine etwas abgewandelte Version: 0 r2@opi ~$ cat x xxxx 0 r2@opi ~$ cat y yyyy 0 r2@opi ~$ cat x | perl -e 'undef $/; print <>;' | cat y - 1<>x 0 r2@opi ~$ cat x yyyy xxxx 0 r2@opi ~$ Der "<>" Operator öffnet ein File zum Lesen und Schreiben. Das File wird nicht auf 0 Bytes gekürzt. "cat" schreibt weiterhin auf STDOUT (Filedescriptor 1). Dieses Vorgehen birgt allerdings immer noch einige Risiken, wenn ein endlicher Buffer ins Spiel kommt. Zum einen ist das resultierende File x größer als das Ausgangsfile. D.h. es kann der Fall eintreten, dass der erste Teil "cat x" Daten liest und liest und liest und da das letzte cat diese Daten immer wieder anhängt, wird das File immer größer. Das erste cat sollte also durch ein Programm ersetzt werden, das genau soviel Bytes liest, wie x ursprünglich groß war. Ich denke man könnte mit "dd" und "stat" da was basteln. Zum anderen ist nicht definiert, ob "cat x" als erstes einen Datenblock liest oder das letzte cat einen schreibt. Es kann also sein, dass schon der erste Lese-Aufruf in "cat x" Daten liest, die direkt vorher von "cat y" geschrieben wurden. Das lässt sich umschiffen, indem man den letzten Teil der Pipe durch folgendes ersetzt: | (sleep 1; cat y -) 1<>x Nun wird das erste cat sicher zuerst ausgeführt, da das zweite erst eine Sekunde später gestartet wird. Eine 3. Schwierigkeit ergibt sich, wenn der Vorspann y recht groß ist. Dann kann es nämlich passieren, dass das "cat y" einen Teil überschreibt, der von "cat x" noch gar nicht gelesen wurde. Der Buffer sollte also mindestens die Größe des Vorspanns haben. (perl -pe 'BEGIN {$x=shift;$|=1;$/=\$x;}' $(stat -c %s y)) Insgesamt würde ich das Problem aber mit einem kleinen Perl-Programm angehen. Torsten
Torsten Foertsch wrote:
On Tuesday 16 May 2006 18:34, GUNREBEN, PETER (Peter) wrote:
Oliver Block schrieb:
Am Dienstag, 16. Mai 2006 10:37 schrieb GUNREBEN, PETER (Peter):
cat LargeFile.txt | buffer | cat ToPrependFile.txt - > LargeFile.txt
würdest Du die Zeile mal etwas erläutern?
ja klar! Also,
1) "cat LargeFile.txt" liest die Datei LargeFile.txt und schreibt ihren Inhalt in die pipe.
2) Der "buffer" liest die pipe, speichert den Inhalt zwischen und schreibt ihn dann in die nächste pipe.
3) "cat ToPrependFile.txt -" liest erst die Datei ToPrependFile.txt und danach STDIN (wird durch das "-" Zeichen gekennzeichnet) und schreibt beides in dieser Reihenfolge nach STDOUT
4) Schliesslich wird STDOUT über "> LargeFile.txt" wieder in die grosse Datei LargeFile.txt zurückgeschrieben.
Ich habe da so meine Zweifel, dass das funktioniert. Leider habe ich das Wunderprogramm buffer nicht. Daher habe ich es durch einen Perl-Einzeiler ersetzt, der den STDIN vollständig einliest und erst dann anfängt auf STDOUT zu schreiben. Das eignet sich sicher nicht für große Files, ist in diesem Fall aber als Grenzfall des Buffers geeignet.
Also ein kleines Experiment und dann einige Erklärungen:
0 r2@opi ~$ cat x xxxx 0 r2@opi ~$ cat y yyyy 0 r2@opi ~$ cat x | perl -e 'undef $/; print <>;' | cat y - >x 0 r2@opi ~$ cat x yyyy 0 r2@opi ~$ cat y yyyy 0 r2@opi ~$
Angenommen x ist das große File, an das am Anfang etwas angehängt (der Inhalt von y) werden soll. Nach dem Experiment enthält x leider nur noch den Inhalt von y. Der eigentliche Inhalt ist verloren, :-(
Der Effekt ist erklärbar, wenn man sich die Kommandozeile näher betrachtet:
Ein-/Ausgabeumleitungen macht die Shell. D.h. das ">x" am Ende wird von der Shell als erstes durchgeführt. Der ">" Operator überschreibt (truncate) ein File. Damit ist x schon überschrieben/leer, wenn das cat-Kommando am Anfang das File öffnet. Ob ein Buffer-Programm dazwischengeschaltet ist, ist dabei unwichtig.
Nun eine etwas abgewandelte Version:
0 r2@opi ~$ cat x xxxx 0 r2@opi ~$ cat y yyyy 0 r2@opi ~$ cat x | perl -e 'undef $/; print <>;' | cat y - 1<>x 0 r2@opi ~$ cat x yyyy xxxx 0 r2@opi ~$
Der "<>" Operator öffnet ein File zum Lesen und Schreiben. Das File wird nicht auf 0 Bytes gekürzt. "cat" schreibt weiterhin auf STDOUT (Filedescriptor 1).
Dieses Vorgehen birgt allerdings immer noch einige Risiken, wenn ein endlicher Buffer ins Spiel kommt.
Zum einen ist das resultierende File x größer als das Ausgangsfile. D.h. es kann der Fall eintreten, dass der erste Teil "cat x" Daten liest und liest und liest und da das letzte cat diese Daten immer wieder anhängt, wird das File immer größer. Das erste cat sollte also durch ein Programm ersetzt werden, das genau soviel Bytes liest, wie x ursprünglich groß war. Ich denke man könnte mit "dd" und "stat" da was basteln.
Zum anderen ist nicht definiert, ob "cat x" als erstes einen Datenblock liest oder das letzte cat einen schreibt. Es kann also sein, dass schon der erste Lese-Aufruf in "cat x" Daten liest, die direkt vorher von "cat y" geschrieben wurden. Das lässt sich umschiffen, indem man den letzten Teil der Pipe durch folgendes ersetzt:
| (sleep 1; cat y -) 1<>x
Nun wird das erste cat sicher zuerst ausgeführt, da das zweite erst eine Sekunde später gestartet wird.
Eine 3. Schwierigkeit ergibt sich, wenn der Vorspann y recht groß ist. Dann kann es nämlich passieren, dass das "cat y" einen Teil überschreibt, der von "cat x" noch gar nicht gelesen wurde. Der Buffer sollte also mindestens die Größe des Vorspanns haben. (perl -pe 'BEGIN {$x=shift;$|=1;$/=\$x;}' $(stat -c %s y))
Insgesamt würde ich das Problem aber mit einem kleinen Perl-Programm angehen.
Torsten
Sehr gut, sehr gut! Du hast völlig Recht. Ich hatte in meiner vorigen Mail etwas übersehen (das Löschen der Datei, bevor sie überhaupt geöffnet wird). Was ich allerdings bei dieser ganzen Diskussion nicht verstehen kann ist, warum nicht - wie sonst an dieser Stelle üblich - eine temporäre Datei mit dem korrekten Inhalt erzeugt wird, die dann nur noch in die zu verlängernde Datei umbenannt werden muß? Diese Vorgehensweise hätte nur eine Vorbedingung, nämlich daß genügend Platz auf der Platte bzw. im Dateisystem vorhanden ist, um die temproäre Datei aufzunehmen, und wäre ansonsten mit keinerlei Risiko behaftet. Cheers Martin
Martin Deppe, Dienstag, 16. Mai 2006 20:57:
Was ich allerdings bei dieser ganzen Diskussion nicht verstehen kann ist, warum nicht - wie sonst an dieser Stelle üblich - eine temporäre Datei mit dem korrekten Inhalt erzeugt wird, die dann nur noch in die zu verlängernde Datei umbenannt werden muß?
Das Verständnis stellt sich bei der Lektüre der Mail des OP ein:
Eine Lösung, die ich sonst manchmal verwende, ist hier auch nicht besonders geeignet, weil dann schon 12GB benötigt werden (wenigstens kurzzeitig): echo "anfang" > temp cat datei >> temp
12 GB gehen ja auch noch, es sei denn, man ist wirklich knapp dran mit Platz und/oder Zeit. Aber laß es mal 12 TB sein. Da ist es wirklich unhandlich, den ganzen Brocken umzukopieren, nur um eine Zeile ganz oben einzufügen. -- Andre Tann
Andre Tann wrote:
Martin Deppe, Dienstag, 16. Mai 2006 20:57:
Was ich allerdings bei dieser ganzen Diskussion nicht verstehen kann ist, warum nicht - wie sonst an dieser Stelle üblich - eine temporäre Datei mit dem korrekten Inhalt erzeugt wird, die dann nur noch in die zu verlängernde Datei umbenannt werden muß?
Das Verständnis stellt sich bei der Lektüre der Mail des OP ein:
Eine Lösung, die ich sonst manchmal verwende, ist hier auch nicht besonders geeignet, weil dann schon 12GB benötigt werden (wenigstens kurzzeitig): echo "anfang" > temp cat datei >> temp
12 GB gehen ja auch noch, es sei denn, man ist wirklich knapp dran mit Platz und/oder Zeit. Aber laß es mal 12 TB sein. Da ist es wirklich unhandlich, den ganzen Brocken umzukopieren, nur um eine Zeile ganz oben einzufügen.
Was machst Du denn mit der anderen Lösung? Genau das selbe ...!!! Solange es kein Dateisystem gibt in dem man vorn in einer Datei etwas einfügen kann (und das können dann auch immer nur Blöcke von 512 Byte oder ganzahlige vielfache davon sein) wird es sich nicht umgehen lassen, den GESAMTEN Inhalt der Datei zu kopieren - so oder so! Gruß Martin
On Wednesday 17 May 2006 09:47, Dominik Klein wrote:
Insgesamt würde ich das Problem aber mit einem kleinen Perl-Programm angehen.
Und was mache ich als des Perl nicht mächtiger Mensch?
An dem beschriebenen Vorgehen herumbasteln oder lernen. Mir ist übrigens für das "cat x" Problem noch folgendes eingefallen. Vielleicht kannst Du es mit mit "head -c $(stat %s x) x" lösen. Voraussetzung ist, dass head an dieser Stelle eine 64-Bit Zahl versteht. Du kannst das mit ls und wc prüfen: head -c $(stat %s x) x | wc -c muss die gleiche Zahl liefern, die ls -l x als Größe anzeigt. Wenn das klappt sind eigentlich alle Probleme gelöst, oder? Torsten
Am Dienstag, 16. Mai 2006 18:34 schrieb GUNREBEN, PETER (Peter):
cat LargeFile.txt | buffer | cat ToPrependFile.txt - > LargeFile.txt
2) Der "buffer" liest die pipe, speichert den Inhalt zwischen und schreibt ihn dann in die nächste pipe.
Dann sparst Du Dir ja im Grunde keinen Speicherplatz, oder? Wie sieht buffer aus? Könnte man STDIN auch einfach mittels exec auf STDOUT umleiten, um es in die pipe zu geben? Gruss, Oliver
participants (7)
-
Andre Tann
-
Dominik Klein
-
GUNREBEN, PETER (Peter)
-
Martin Deppe
-
Oliver Block
-
Thomas Hertweck
-
Torsten Foertsch