RE: Etwas an den Anfang einer sehr großen Textdatei einfügen
Oliver, Oliver Block schrieb:
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?
Was meinst Du damit? Dominik hat doch das Problem, dass er auf der Platte keine 12 GByte reservieren will, nur damit er die 6 GByte grosse Datei umkopieren kann. Über die "buffer" Lösung wird ja lediglich ein Teil der Datei im RAM gehalten und auf der Platte werden nur 6 GByte benötigt.
Wie sieht buffer aus? Könnte man STDIN auch einfach mittels exec auf STDOUT umleiten, um es in die pipe zu geben?
Kannst Du 'mal genauer erklären, was Du vor hast? Ich muss uebrigens leider zugeben, dass meine Lösung wirklich nicht sicher ist und daher mit Vorsicht zu geniessen ist. Das Problem ist, dass "cat ToPrependFile.txt" unter Umständen schon anfängt zu schreiben, bevor der buffer die Datei im Speicher hat. Um das zu verhindern habe ich folgendes probiert: cat ToPrependFile.txt LargeFile.txt | buffer -p 100 > LargeFile.txt Leider funktioniert die obige Zeile auch nicht sicher, wobei ich nicht verstehe, warum. Die "-p 100" Option sollte den buffer dazu bringen, erst dann zu schreiben, wenn der buffer voll ist (default 1 MByte). Wenn nun ToPrependFile.txt deutlich kleiner als 1 MByte ist, hätte ich erwartet, dass es problemlos funktioniert. Das ist aber leider nicht der Fall. Hat jemand eine Erklärung? Gruss, Peter.
GUNREBEN, PETER (Peter) wrote: >Oliver, > >Oliver Block schrieb: > > >>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? >> >> > >Was meinst Du damit? Dominik hat doch das Problem, dass er >auf der Platte keine 12 GByte reservieren will, nur damit er >die 6 GByte grosse Datei umkopieren kann. Über die "buffer" >Lösung wird ja lediglich ein Teil der Datei im RAM gehalten >und auf der Platte werden nur 6 GByte benötigt. > > Hallo Leute, da die obige Lösung m.E. ein zu großes Risiko birgt, daß die Daten dabei zerstört werde, schlage ich folgendes vor: Man schreibe ein kleines Programm (ich würde dies in C tun, aber es ginge sicher auch per "dd"), welches eine (große) Datei, vor die eine (beliebig) große (idealerweise aber kleinere) andere Datei kopiert werden soll, derart bearbeitet, daß erst einmal alle Daten von HINTEN NACH VORN (also Rückwärts) so weit weiter nach hinten kopiert werden, daß der neue Datenblock (sprich, die Datei) vorn (natürlich exakt) hineinpaßt. Dies ist m.E. die einzige Möglichkeit, nicht vorrübergehend doppelt soviel Platz zu benötigen, wie die beiden Dateien zusammen haben und gleichzeitig kein Risiko einzugehen (Prgrammfehler mal beiseite gelassen), die Daten zu zerstören. Ich habe bzw. mußte diese Vorgehensweise wählen, als ich eine allgemeine Routine (in Assembler) schrieb, um Datenblöcke im Speicher beliebig zu verschieben/kopieren. (funkioniert übrigens prächtig) ;-) Gruß Martin
Hallo, Am Fre, 19 Mai 2006, Martin Deppe schrieb:
cat LargeFile.txt | buffer | cat ToPrependFile.txt - > LargeFile.txt [..] da die obige Lösung m.E. ein zu großes Risiko birgt, daß die Daten dabei zerstört werde, schlage ich folgendes vor:
Das Risiko betraegt 100%. Es ist *garantiert*, dass die Ausgabedatei nur die ersten N Bytes enthaelt. Der Grund ist der gleiche wie immer, warum man nicht aus einer Datei lesen und in diese umleiten kann. Denn bei der Umleitung wird die Datei geoeffnet und _beim oeffnen_ geleert! Geschrieben wird dann nur das, was in der Pipe / im buffer steht. -dnh -- Braucht Ihr ab oder zunehmen ??? Ich benutze zur Zeit ein total tolles Zeug um abzunehmen. Es geht irre schnell und ist wunderbar. Es ist auch gebrauchlich zum zunehmen. Ihr koenntet auch business tun.....interestan nicht war ??? ['disa_linn@my-deja.com spammt mit viel Hirn in dag°]
On Friday 19 May 2006 11:50, GUNREBEN, PETER (Peter) wrote:
Was meinst Du damit? Dominik hat doch das Problem, dass er auf der Platte keine 12 GByte reservieren will, nur damit er die 6 GByte grosse Datei umkopieren kann. Über die "buffer" Lösung wird ja lediglich ein Teil der Datei im RAM gehalten und auf der Platte werden nur 6 GByte benötigt.
Wie sieht buffer aus? Könnte man STDIN auch einfach mittels exec auf STDOUT umleiten, um es in die pipe zu geben?
Kannst Du 'mal genauer erklären, was Du vor hast?
Ich muss uebrigens leider zugeben, dass meine Lösung wirklich nicht sicher ist und daher mit Vorsicht zu geniessen ist. Das Problem ist, dass "cat ToPrependFile.txt" unter Umständen schon anfängt zu schreiben, bevor der buffer die Datei im Speicher hat. Um das zu verhindern habe ich folgendes probiert:
cat ToPrependFile.txt LargeFile.txt | buffer -p 100 > LargeFile.txt
Leider funktioniert die obige Zeile auch nicht sicher, wobei ich nicht verstehe, warum. Die "-p 100" Option sollte den buffer dazu bringen, erst dann zu schreiben, wenn der buffer voll ist (default 1 MByte). Wenn nun ToPrependFile.txt deutlich kleiner als 1 MByte ist, hätte ich erwartet, dass es problemlos funktioniert. Das ist aber leider nicht der Fall.
Hat jemand eine Erklärung?
1) ja, ">LargeFile.txt" kürzt das File auf 0 Bytes bevor irgendwas anderes passiert. 2) Du liest und schreibst nach LargeFile.txt gleichzeitig, wobei der Lesezeiger kleiner als der Schreibezeiger ist. Du liest aber, bis ein End-of-File auftaucht. Ohne Buffering kann das eigentlich nicht passieren. D.h. die Größe Deines Output-Files ist ziemlich zufällig. Hier eine Lösung die funktioniert. Ich habe es mit einem 5GB File namens 'big' und einem 1MB 'prefix' unter Suse 10.0 (32bit) getestet. Ich hatte dazu alles in eine Zeile geschrieben und den Backslash am Ende der vorletzten Zeile weggelassen. perl -e 'my $prefix=shift; my $file=shift; $prefix=do {local $/; local @ARGV=($prefix); <>}; my $fs=(stat($file))[7]; $/=\length $prefix; open my $f, $file; while( my $buf=<$f> and $fs>0 ) { print $prefix; $prefix=$buf; $fs-=length $buf; $/=\$fs if(${$/}>$fs); } print $prefix' \ prefix big 1<>big Erklärung: "1<>big" am Ende öffnet STDOUT zum Lesen und Schreiben. Damit wird das Output-File nicht schon von der Shell gelöscht und so das erste Problem behoben. Mit der Option -e kann man dem Perl-Interpreter im nächsten Argument ein Kommando übergeben, ähnlich wie "bash -c ...". Perl wird hier mit 4 Parametern aufgerufen: 1) -e, 2) das Kommando 3) das Prefix-File prefix, 4) das große File big. Die Output-Umleitung wird komplett von der Shell bearbeitet. Perl bekommt das nicht mit. Die ersten 2 Zeilen des Kommandos lesen die Parameter prefix und big ein und speichern sie in den Variablen $prefix und $big. Die 3. Zeile liest das prefix-File dann vollständig in die Variable $prefix ein. Zeile 4 ermittelt die Größe in Bytes von 'big'. Zeile 5 setzt die Input-Buffer Größe ($/) auf die Länge des Prefix. Ziel ist es immer einen solchen Block zwischen Lese- und Schreibezeiger Platz zu lassen. Dann öffne ich das Input-File 'big'. Der Zyklus, der in Zeile 7 beginnt, liest 'big' nun blockweise ein. Das Ende ist erreicht, wenn entweder keine Daten mehr da sind oder die Größe der noch zu lesenden Daten ($fs) kleiner oder gleich 0 wird. In Zeile 8 wird der aktuelle Prefix (oder Output-Buffer) ausgegeben. Da $prefix nun nicht mehr benötigt wird, kann die Variable mit dem gerade gelesenen Block in Zeile 9 überschrieben werden. In Zeile 10 wird nun die gelesene Länge von der Gesamtlänge ($fs) abgezogen. Falls $fs nun kleiner als die Blockgröße wurde, ist eigentlich das Ende erreicht. Das Programm muss noch einen Block einlesen, der aber kleiner als die normale Blockgröße ist. Also wird die Blockgröße auf diesen Wert gesetzt. Nach dem Zyklus in Zeile 13 enthält $prefix noch einen noch nicht geschriebenen Block. Dieser muss also noch ausgegeben werden. Das war's. Zum Test: (dd if=<(yes) bs=1k count=5242880; echo y) >big dd if=<(yes n) bs=1k count=1024 >prefix Jetzt enthält big 2684354561 Zeilen bestehend aus 2 Zeichen: "y\n" und prefix 524288 Zeilen "n\n". big ist damit 2 Bytes größer als 5GiB, prefix genau 1MiB groß: r2@ikra:~> ls -l big prefix -rw-r--r-- 1 r2 users 5368709122 2006-05-19 15:39 big -rw-r--r-- 1 r2 users 1048576 2006-05-19 14:12 prefix Während nun das Programm läuft ändert sich die Größe von big übrigens nicht. Es bleibt die meist Zeit 5GiB+2Bytes groß. Erst am Schluß wächst es. Nach dem Perl-Programm ist big um genau ein MiB gewachsen: r2@ikra:~> ls -l big -rw-r--r-- 1 r2 users 5369757698 2006-05-19 16:04 big r2@ikra:~> bc -lq 5369757698-5368709122 1048576 Zur Kontrolle, ob die Bytes auch wirklich so liegen, wie sie sollen, können wir aufgrund der Struktur von big und Prefix das Kommando "uniq -c" benutzen. Es zählt gleiche benachbarte Zeilen und gibt die Summe jeweils eines Blocks gleicher Zeilen aus: r2@ikra:~> uniq -c big 524288 n 2684354561 y Torsten
participants (4)
-
David Haller
-
GUNREBEN, PETER (Peter)
-
Martin Deppe
-
Torsten Foertsch