Alexander Foken: Kleines Problem beim Kopieren von Dateien mit ein CGI Script

Beitrag lesen

Moin Moin !

binmode(Ziel) if (-B Quelle);

Ziel und Quelle sind sehr unglücklich gewählte Namen für Filehandles, es ist Konvention, Filehandles komplett groß zu schreiben: ZIEL und QUELLE.

Aus der Doku: The -T and -B switches work as follows. The first block or so of the file is examined for odd characters such as strange control codes or characters with the high bit set. If too many strange characters (>30%) are found, it's a -B file, otherwise it's a -T file. Also, any file containing null in the first block is considered a binary file. If -T or -B is used on a filehandle, the current stdio buffer is examined rather than the first block. Both -T and -B return true on a null file, or a file at EOF when testing a filehandle. Because you have to read a file to do the -T test, on most occasions you want to use a -f against the file first, as in next unless -f $file && -T $file.

Oder kurz: Perl *rät*, ob QUELLE binär ist oder nicht. Und es kann *falsch* *raten*. Genau das passiert offensichtlich bei Dir. Wenn Du eine Datei kopieren willst, willst Du sie *immer* binär kopieren, so lange sie auf dem System bleibt. Also schlicht "binmode QUELLE; binmode ZIEL;".

Zum Rest deines Codes:

sub kopieren()
            ^^-- *so* hat die Funktion keine Argumente ...
{
 my $quelle = shift;
 my $ziel = shift;
   ^-- aber Du versuchst, gleich zwei zu bekommen. Das kann nur zufällig funktionieren.

"sub kopieren($$)" ist richtig, "sub kopieren" ganz ohne Klammern geht auch, dann ist aber nicht garantiert, daß Du überhaupt Argumente bekommst.

Benutze das w-Flag und das strict-Pragma, dann passieren solche Fehler nicht mehr:

#!/usr/bin/perl -w
use strict;

Du prüfst nicht, ob open() fehlschlägt. "unless -d $quelle" ist keine ausreichende Prüfung, ob $quelle lesbar ist.

Standard-Floskel:

open FILEHANDLE,"<$filename" or die "Error reading $filename: $!";

Eine Datei *byteweise* zu kopieren ist so ziemlich das ineffizienteste, was man programmieren kann. Lies mit der read-Funktion größere Blöcke (1024 Bytes, 4096 Bytes, 65536 Bytes - je nach dem, wie viel Speicher Du nutzen willst), sobald die read-Funktion weniger als die Blockgröße liest, hast Du das wahrscheinlich Dateiende erreicht. Ganz sauber läßt man eine while-Schleife laufen, bis die read-Funktion 0 oder undef zurückliefert. eof() brauchst Du nicht, das steht auch in der Doku. "Practical hint: you almost never need to use eof in Perl, because the input operators typically return undef when they run out of data, or if there was an error."

my $buffer;
  while (read(QUELLE,$buffer,1024)) {
    print ZIEL $buffer;
  }

Wenn die Speicherbelastung nebensächlich ist, geht es auch so:

print ZIEL while <QUELLE>;

Das ist "echtes" Perl: Kurz, knapp, effektiv, und lesbar. Übrigens auch so in der Doku zu finden. Und ja, es kopiert die gesamte Datei!

So, das war jetzt das längste RTFM, daß ich in den letzten Wochen geschrieben habe. Tu es! Lies die exzellente Dokumentation zu Perl! Es hilft!

Und dann versuch nicht, an Trivialitäten wie dem stumpfen Kopieren von Dateien herumzuoptimieren. Das wirst Du mit Perl ohnehin nicht schaffen. Denn wie Du der Doku von File::Copy entnehmen kannst, wird sehr oft das Betriebssystem beauftragt (Stichwort: syscopy), die Datei zu kopieren. Und das passiert bestenfalls in optimiertem Assembler-Code. Da hast Du mit reinem Perl nicht den Hauch einer Chance. Es gibt übrigens auch Module, um ganze Dateibäume zu kopieren - mit einem einzigen Funktionsaufruf.

Wenn Du Perl lernen willst, lies den (meistens sehr gut kommentierten) Code der Module, die Perl schon beiliegen. Und den Code der Module, die Du außerdem brauchst. Das Rad tausend mal neu zu erfinden macht es nicht besser. Du bist dann einfach nur dazu verdammt, alle Fehler noch einmal zu machen, die andere längst beseitigt haben. Konzentriere Dich auf Dein Kernproblem, optimiere dort, und für den Rest gibt es gut getestete Module.

Und erst wenn Du 100% sicher bist, daß Dein Code fehlerfrei ist und etwas trotzdem nicht funktioniert, mußt Du überlegen, ob vielleicht nicht doch ein Modul defekt ist. Auch in gut getesteten Modulen können manchmal, sehr selten, echte Design-Fehler vorkommen. Ich habe in den letzten drei Jahen gerade einmal einen gefunden: http://www.rosat.mpe-garching.mpg.de/mailing-lists/dbi/2000-02/msg00375.html (Thread verfolgen)

Alexander

--
Nein, ich beantworte keine Fragen per eMail. Dafür ist das Forum da.
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so!"
Mein "Lieblings-Forums-Bug": http://cforum.teamone.de/phpbt/bug.php?op=show&bugid=103&pos=2