Alex Wolff: ganze verzeichnis-bäume verschieben

Hallo!
Ich will ein Perl-Programm schreiben, welches sämtliche Dateien und Unter-Verzeichnisse eines bestimmten Verzeichnisses in ein anderes Verzeichnis verschiebt. oder kurz gesagt: ein ganzer verzeichnis-baum soll verschoben werden.
Eine Idee zur Realisierung hab ich schon: mit opendir und readdir die einzelnen Verzeichnisse rekursiv durchgehen, gleichnamige Unter-Verzeichnisse im Ziel-Verzeichnis anlegen, Dateien kopieren, anschließend die kopierten Dateien löschen (rm), anschließend die leeren Verzeichnisse löschen (rmdir).
So weit so gut, aber ziemlich umständlich, oder?
Ich hatte mir Hoffnung gemacht, daß es mit den Modulen File::Copy und File::Path viel einfacher sein könnte, allerdings komm ich mit der Syntax nicht zurecht.
Daher meine Frage: Läßt sich diese Funktion (Verzeichnis-Baum verschieben) irgendwie mit den File::-Modulen einfacher realisieren oder gibt es vielleicht schon ein Modul, was genau diese Funktionalität anbietet?
Merci im Voraus,
alex wolff

  1. Hi,

    Ich will ein Perl-Programm schreiben, welches [...]

    da Du den Bereich CGI gewählt hast: Schreibe es so, dass es außerhalb des CGI-Kontextes funktioniert, und erweitere es dann so, dass es CGI-tauglich ist.

    Soll heißen: Das Problem hat mit CGI nichts zu tun, sondern betrifft nur Perl :-)

    Eine Idee zur Realisierung hab ich schon: mit opendir und readdir [...]

    Warum nicht mit File::Find? Gut, leere Verzeichnisse werden nicht mit kopiert, das ist wahr.

    So weit so gut, aber ziemlich umständlich, oder?

    Ja :-)

    Ich hatte mir Hoffnung gemacht, daß es mit den Modulen File::Copy und File::Path viel einfacher sein könnte, allerdings komm ich mit der Syntax nicht zurecht.

    Wo genau liegen denn dabei Deine Probleme?

    Cheatah

    1. Hi,

      Ich will ein Perl-Programm schreiben, welches [...]

      da Du den Bereich CGI gewählt hast: Schreibe es so, dass es außerhalb des CGI-Kontextes funktioniert, und erweitere es dann so, dass es CGI-tauglich ist.

      Soll heißen: Das Problem hat mit CGI nichts zu tun, sondern betrifft nur Perl :-)

      äh, ja, da bin ich etwas überfragt beim unterschied zwischen perl und cgi. ich benutze perl sowieso nur für die internet-programmierung. meine .pl-dateien werden im cgi-bin-verzeichnis abgelegt und - was wahrscheinlich die Hauptursache meines Fauxpas war - ich hab bisher alle meine probleme mit Fragen aus dem CGI-Bereich lösen können. ich wußte gar nicht, daß es einen eigenen Perl-Bereich gibt.
      aber vielleicht hätte ich Perl einfach gar nicht erwähnen sollen, dann hät's schon gepasst, nich ?  ;)

      Eine Idee zur Realisierung hab ich schon: mit opendir und readdir [...]

      Warum nicht mit File::Find? Gut, leere Verzeichnisse werden nicht mit kopiert, das ist wahr.

      aha.
      mir wärs aber lieber, wenn leere Verzeichnisse auch mitkopiert werden. danke für tip!

      Ich hatte mir Hoffnung gemacht, daß es mit den Modulen File::Copy und File::Path viel einfacher sein könnte, allerdings komm ich mit der Syntax nicht zurecht.

      Wo genau liegen denn dabei Deine Probleme?

      Ich verstehe nicht wie man statt nur eine Datei / einem Verzeichnis , mehrere kopiert / anlegt.
      Aber das besondere an diesen Modulen ist doch gerade, daß man mit einem Befehl ganze Bäume anlegt oder absägt.
      Ich hab mir gerade die Beschreibung für das File::Find-Modul durchgelesen und versteh jetzt - glaub ich - noch weniger ;)

      find(&wanted, '/foo', '/bar');

      Ich vermute mal, die Zeichenketten '/foo' und '/bar' sind irgendwie dazu da, um das/die zu durchsuchende/n Verzeichnis/se anzugeben. Konnte keine Info dazu finden... :(

      Anscheinend lässt sich mit diesem Modul fast alles machen. Ich stell es mir so vor:

      Wenn also

      $moveroot = 'cgi-bin/sub1/sub2/sub3';

      das zu verschiebende Verzeichnis ist, und

      $movedestination = 'cgi-bin/othersub';

      ist das Verzeichnis, in welches verschoben werden soll,
      dann schreibe ich:

      use File::Find;
      use File::Copy;
      use File::Path;

      find(&wanted, $moveroot);

      In der Unterfunktion wanted werden dann eine Reihe von Befehlen ausgeführt, die mit den gefundenen Dateien/Unterverzeichnissen irgendwas anstellen. Nämlich 1.: Ein Verzeichnis gleichen Namens in $movedestination anlegen
      und 2.: Die Dateien aus diesem Verzeichnis verschieben.

      sub wanted
      {
      no_chdir = true; # $_ setzt sich aus Pfad _und_ Namen zusammen
      if (-d $_)
         {
         mkpath($movedestination.'/'.$_, 0, 0777);
         find(&wanted, $moveroot.'/'.$_); # find() rekursiv aufrufen für Unterverzeichnis
         }
      if (-f $_)
         {
         move($_,$movedestination.'/'.$_);
         }
      }

      Anschließend wird das Verzeichnis $moveroot entfernt:

      rmtree($moveroot, 0, 0);

      Kann so etwas funktionieren? Ist meine Syntax überhaupt richtig? Die Beschreibungen zu den Modulen sind so was kryptisch...
      alex

      1. Hallo,

        äh, ja, da bin ich etwas überfragt beim unterschied zwischen perl und cgi.

        Perl ist eine Scriptsprache, und CGI ein Datenübertragungsmechanismus zwischen Webservern und Scripts/Programme, welche vom Webserver gegebenenfalls aufgerufen werden.

        ich benutze perl sowieso nur für die internet-programmierung.

        Mit Perl kann man allerdings wesentlich mehr machen, als nur Scripts für den Webserver.

        aber vielleicht hätte ich Perl einfach gar nicht erwähnen sollen, dann hät's schon gepasst, nich ?  ;)

        Da Du ein Problem hast, daß Du in Perl lösen willst, ist des mehr als angebracht, es auch zu erwähnen. Was hättest Du davon, wenn Dir jemand sagen würde, wie es mit PHP oder C oder... was auch immer geht.

        Grüße
          Klaus

  2. Hi Alex,

    Ich will ein Perl-Programm schreiben, welches sämtliche Dateien und
    Unter-Verzeichnisse eines bestimmten Verzeichnisses in ein anderes
    Verzeichnis verschiebt. oder kurz gesagt: ein ganzer verzeichnis-baum
    soll verschoben werden.

    falls Du wirklich "verschoben" meinst und mit "anderes Verzeichnis" wirk-
    lich nur "Verzeichnis" und nicht "Laufwerk" oder was auch immer meinst,
    dann verstehe ich Dein Problem nicht so recht.
    Sowohl "mv" unter UNIX als auch "move" unter Windows verschieben das
    Wurzelverzeichnis inklusive des gesamten Baums darunter - fertig.

    Das funktioniert natürlich nur, solange durch die Verschiebung alleine
    durch eine Neu-Verkettung des Verzeichnisbaums möglich ist, d. h. solange
    die Dateien und Verzeichnisse an ihrem ursprünglichen Ort bleiben dürfen.

    Müssen diese Daten dagegen auf ein anderes Laufwerk übertragen werden,
    dann beginnt Dein Problem, plattformspezifisch zu werden:

    • Unter UNIX erkennt das Betriebssystem automatisch, daß es nun kopieren
        und löschen muß;
    • unter Windows scheint es (nach einem gerade durchgeführten flüchtigen
        Versuch) einfach nicht möglich zu sein, 'move' zu verwenden, und Du
        müßtest in diesem Falle tatsächlich das tun, was Du ohnehin tun woll-
        test: Explizit kopieren und löschen.

    Eine Idee zur Realisierung hab ich schon: mit opendir und readdir die
    einzelnen Verzeichnisse rekursiv durchgehen, gleichnamige Unter-
    Verzeichnisse im Ziel-Verzeichnis anlegen, Dateien kopieren,
    anschließend die kopierten Dateien löschen (rm), anschließend die
    leeren Verzeichnisse löschen (rmdir).
    So weit so gut, aber ziemlich umständlich, oder?

    Auch das kommt m. E. wieder auf Deine Plattform an.

    SunOS (und wahrscheinlich alle UNIXe) unterstützt im 'mkdir'-Befehl
    den Parameter '-p', so daß Du mit einem Befehl einen kompletten Pfad
    anlegen kannst, statt rekursiv jedes einzelne Verzeichnis anzulegen.
    Weil 'mkdir -p' alles in einem Rutsch macht, ist es insbesondere auch
    noch wesentlich performanter als viele einzelne 'mkdir's.

    Zweiter Tip: Wenn Du sämtliche Pfade von anzulegenden Verzeichnissen
    hast, sammele sie in einem Hash auf (sofern Du Dir das vom RAM-Bedarf
    her leisten kannst ...) und eliminiere anschließend alle Präfixe von
    bereits existierenden Pfaden. Damit erhältst Du die minimale Anzahl von
    'mkdir -p'-Befehlen, um Deinen kompletten Baum zu erzeugen.

    Das Löschen eines Baums geht unter UNIX mit 'rm -rf <wurzel>' wesentlich
    einfacher, als den ganzen Baum selbst zu traversieren.

    oder gibt es vielleicht schon ein Modul, was genau diese Funktionalität
    anbietet?

    Wie gesagt: Zuerst solltest Du prüfen, ob ein einfaches Umbenennen des
    Wurzelverzeichnisses nicht schon ausreicht.

    Alle meine Lösungsvorschläge bezogen sich auf Betriebssystemfunktionen,
    nicht auf Perl. Dein Anwendungsfall ist aber auch sehr betriebssystem-nah;
    gerade deshalb solltest Du nicht unbedingt versuchen, etwas in Perl nach-
    zubauen, was das Betriebssystem ohnehin können muß.
    (Es sei denn, Plattformabhängigkeit ist in Deinem Falle wesentlich; sie
    kann Dich aber hier einen nennenswerten Faktor an Performance kosten.)

    Viele Grüße
          Michael

    1. Hi Michael,

      falls Du wirklich "verschoben" meinst und mit "anderes Verzeichnis" wirk-
      lich nur "Verzeichnis" und nicht "Laufwerk" oder was auch immer meinst,
      dann verstehe ich Dein Problem nicht so recht.
      Sowohl "mv" unter UNIX als auch "move" unter Windows verschieben das
      Wurzelverzeichnis inklusive des gesamten Baums darunter - fertig.

      aaachsoo! dann reichts also, ich schreibe:

      use File::Copy;
      move("/folder_x/subfolder_x","/folder_y/subfolder_y");

      und schon wandert das Verzeichnis subfolder_x mit allen Unterverzeichnissen in das Verzeichnis subfolder_y ?!?
      Das muss ich sofort ausprobieren!
      Danke schön!

      Alle meine Lösungsvorschläge bezogen sich auf Betriebssystemfunktionen,
      nicht auf Perl. Dein Anwendungsfall ist aber auch sehr betriebssystem-nah;
      gerade deshalb solltest Du nicht unbedingt versuchen, etwas in Perl nach-
      zubauen, was das Betriebssystem ohnehin können muß.
      (Es sei denn, Plattformabhängigkeit ist in Deinem Falle wesentlich; sie
      kann Dich aber hier einen nennenswerten Faktor an Performance kosten.)

      Das Programm soll mal im Internet laufen, deshalb kann ich wohl von einem UNIX-System ausgehen. Hier bei mir wird es mit OmniHTTPd auf Win98 lokal getestet. Da aber nicht auf andere Laufwerke verschoben werden soll, dürfte es no probs geben.
      thnx noch mal,
      alex

      1. Hallo,

        Sowohl "mv" unter UNIX als auch "move" unter Windows verschieben das
        Wurzelverzeichnis inklusive des gesamten Baums darunter - fertig.
        aaachsoo! dann reichts also, ich schreibe:

        use File::Copy;
        move("/folder_x/subfolder_x","/folder_y/subfolder_y");

        Na ja, sooo gehts nun auch wieder nicht. Hättest Du vor diesem Posting einen flüchtigen Blick in die Dokumenation von File::Copy geworfen, dann hättest Du aich sicher gesehen, daß dort keine Funktion namens 'move' implementiert ist.

        Solange das Verschieben im gleichen Filesystem (also für Windowsfreunde 'Laufwerk') passiert, gibt es sogar eine Perlfunktion, die das erledigen kann.
        Und weil wir hier ja nicht immer alles vorkauen wollen, hab' ich mir gedacht, daß es noch ein bißchen spanndend bleiben soll.
        Also, für viele modernere Betriebssysteme sind Verzeichnisse nichts anderes als spezielle Dateien. Und Dateien kann man ja bekanntlich auch _umbenennen_.
        Einige Systeme, Unix/Linux z.B., und wider Erwarten auch Windows, bieten auch die Möglichkeit an, eine Datei inklusive des Pfades umzubenennen. Was wiederum einem Verschieben gleichkommt.

        Wie gesagt, funktioniert nicht auf jedem Betriebssystem, und nur, wenn die Dateisystemgrenzen nicht übersprungen werden.
        (Linux und Windows 2000 getestet, und für gut empfunden)

        Na weißt Du schon, wie die Funktion fürs _Umbenennen_ heißt? Ein kleiner Tip noch, der Name beginnt mit 're' und sie hat zwei Übergabeparameter.

        Und wenn das nich ausreicht, dann kannst Du Dir aus File::Copy ja immer noch #copydir' ansehen, das erledigt zumindest den halben Weg. Und da Dich das Thema interessiert, kannst Du dort auch nachsehen, wie so etwas gemacht werden kann.

        Grüße
          Klaus

        1. Hallo Klaus!

          use File::Copy;
          move("/folder_x/subfolder_x","/folder_y/subfolder_y");

          Na ja, sooo gehts nun auch wieder nicht. Hättest Du vor diesem Posting einen flüchtigen Blick in die Dokumenation von File::Copy geworfen, dann hättest Du aich sicher gesehen, daß dort keine Funktion namens 'move' implementiert ist.

          Ich _habe_ einen mehr als flüchtigen Blick da reingeworfen und Tatsache ist: mit File::Copy hat man die Funktion copy _und_ die Funktion move. gesehen bei:
          http://aspn.activestate.com/ASPN/Reference/Products/ActivePerl/lib/File/Copy.html
          Obere Zeile hatte ich direkt aus der Doku-Seite rauskopiert und nur die Ordner anders benannt.

          Solange das Verschieben im gleichen Filesystem (also für Windowsfreunde 'Laufwerk') passiert, gibt es sogar eine Perlfunktion, die das erledigen kann.
          Und weil wir hier ja nicht immer alles vorkauen wollen, hab' ich mir gedacht, daß es noch ein bißchen spanndend bleiben soll.
          Also, für viele modernere Betriebssysteme sind Verzeichnisse nichts anderes als spezielle Dateien. Und Dateien kann man ja bekanntlich auch _umbenennen_.
          Einige Systeme, Unix/Linux z.B., und wider Erwarten auch Windows, bieten auch die Möglichkeit an, eine Datei inklusive des Pfades umzubenennen. Was wiederum einem Verschieben gleichkommt.

          Wie gesagt, funktioniert nicht auf jedem Betriebssystem, und nur, wenn die Dateisystemgrenzen nicht übersprungen werden.
          (Linux und Windows 2000 getestet, und für gut empfunden)

          Na weißt Du schon, wie die Funktion fürs _Umbenennen_ heißt? Ein kleiner Tip noch, der Name beginnt mit 're' und sie hat zwei Übergabeparameter.

          Und wenn das nich ausreicht, dann kannst Du Dir aus File::Copy ja immer noch #copydir' ansehen, das erledigt zumindest den halben Weg. Und da Dich das Thema interessiert, kannst Du dort auch nachsehen, wie so etwas gemacht werden kann.

          hast du heute was schlechtes gegessen?

          alex

          1. Hallo,

            Ich _habe_ einen mehr als flüchtigen Blick da reingeworfen und Tatsache ist: mit File::Copy hat man die Funktion copy _und_ die Funktion move. gesehen bei:
            http://aspn.activestate.com/ASPN/Reference/Products/ActivePerl/lib/File/Copy.html
            Obere Zeile hatte ich direkt aus der Doku-Seite rauskopiert und nur die Ordner anders benannt.

            Da sieht man mal wieder, was sich zwischen den einzelnen Versionen ändern kann. In der Version 2.0 des Moduls gab es kein 'move' aber dafür ein 'copydir'.
            Du solltest daher gegebenenfalls die Version des Moduls prüfen.

            hast du heute was schlechtes gegessen?

            Im Gegenteil, danz vorzüglich. Wie kommst Du nur auf diese Idee?

            Grüße
              Klaus