Verzeichnis kopieren + überschreiben

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
rumpelwicht
User
Beiträge: 3
Registriert: Mittwoch 26. März 2008, 05:03

Hallo zusammen!

Ich suche nach einer einfachen Möglichkeit ein ganzes Verzeichnis (also einschliesslich der darin enthaltenen Dateien und Unterverzeichnisse usw.) von einem Ort zun einem anderen zu kopieren und dabei eventuell im Zielort bereits existierende Verzeichnisse und Dateien zu überschreiben.

Im Grunde also das gleiche was shutil.move macht nur als kopieren und nicht verschieben und dabei eben überschreiben.

Hat da jemand bitte eine Idee? Die Forumssuche und meine Internetrecherche haben leider nicht weitergeholfen. Vielen Dank vorab!
Jan-Peer
User
Beiträge: 166
Registriert: Dienstag 2. Oktober 2007, 10:55

Die Lösung dürfte shutil.copytree() sein:

http://docs.python.org/lib/module-shutil.html
rumpelwicht
User
Beiträge: 3
Registriert: Mittwoch 26. März 2008, 05:03

Leider nein, da hier vorausgesetzt wird, dass das Zielverzeichnis noch nicht existiert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Dann nutze rmtree bevor du Kopierst ;-)
Jan-Peer
User
Beiträge: 166
Registriert: Dienstag 2. Oktober 2007, 10:55

Hyperion hat geschrieben:Dann nutze rmtree bevor du Kopierst ;-)
Das geht nur so lange gut, wie im Zielverzeichnis keine anderen erhaltenswerten Daten rumschwirren ...

Im Augenblick sehe ich für diesen Fall keine andere Möglichkeit, als sich mit os.path.walk was zu basteln.
rumpelwicht
User
Beiträge: 3
Registriert: Mittwoch 26. März 2008, 05:03

Danke euch für den Tipp, werde mich also selbst an die Arbeit machen.
Shpongle
User
Beiträge: 1
Registriert: Donnerstag 27. März 2008, 17:46

Ich habe vor nicht allzulange Zeit sowas gebastelt...
Der Code dürfte etwas mangelhaft sein und es werden keinerlei Fehler abgefangen, aber vielleicht ist es trozdem irgendwie hilfreich...

Code: Alles auswählen

import os
import shutil
import filecmp

def RelativePath(base,path):
    return path.replace(base + os.sep,'',1)

def CopyNewContent(src,dst):
    mkdir = copy = 0
    for root, folders, files in os.walk(src,topdown=True):
        for foldername in folders:
            srcpath = os.path.join(root,foldername)
            relpath = RelativePath(src,srcpath)
            dstpath = os.path.join(dst,relpath)
            if not os.path.isdir(dstpath):
                mkdir +=1
                print '%-6s %s' % ('mkdir', relpath)
                os.mkdir(dstpath)
        for filename in files:
            srcpath = os.path.join(root,filename)
            relpath = RelativePath(src,srcpath)
            dstpath = os.path.join(dst,relpath)
            if not os.path.isfile(dstpath) or not filecmp.cmp(srcpath,dstpath):
                copy += 1
                print '%-6s %s' % ('copy', relpath)
                shutil.copy2(srcpath,dstpath)
    return ('mkdir', mkdir), ('copy', copy)

def DeleteOldContent(src,dst):
    rmdir = remove = 0
    for root, folders, files in os.walk(dst,topdown=False):
        for foldername in folders:
            dstpath = os.path.join(root,foldername)
            relpath = RelativePath(dst,dstpath)
            srcpath = os.path.join(src,relpath)
            if not os.path.isdir(srcpath):
                rmdir += 1
                print '%-6s %s' % ('rmdir', relpath)
                os.rmdir(dstpath)
        for filename in files:
            dstpath = os.path.join(root,filename)
            relpath = RelativePath(dst,dstpath)
            srcpath = os.path.join(src,relpath)
            if not os.path.isfile(srcpath):
                remove += 1
                print '%-6s %s' % ('remove', relpath)
                os.remove(dstpath)
    return ('rmdir', rmdir), ('remove', remove)

def Sync(src,dst):
    return CopyNewContent(src,dst) + DeleteOldContent(src,dst)

# Beispiel
if __name__=="__main__":
    x = Sync('/mnt/win/Music','/home/daniel/Music')
    for action, num in x:
        print '%-6s %-4d' % (action, num)
P.S. Mein erster Post ^^
Baracke
User
Beiträge: 2
Registriert: Mittwoch 9. Juli 2008, 10:37

Servus,

ich weiss der Thread ist schon älter. Jedoch hab ich nach einer ähnlichen Funktionalität gesucht (brauche nur die Dateien von einem Verzeichniss zu einem anderem kopieren, nicht dessen Unterordner) und im Forum dazu nichts direkt gefunden.

Der Tip, man solle einfach in shutil.py reinschauen wie es da gemacht wird hat mir dann letztlich geholfen. Deswegen will ich mal eine modifizierte copytree Funktion posten, welche auch in vorhande Zielverzeichnisse kopieren kann.

So aber erstmal schauen wir uns die orginal copytree Funktion von shutil.py (Python 2.5.1) an:

Code: Alles auswählen

def copytree(src, dst, symlinks=0):
    """Recursively copy a directory tree using copy2().

    The destination directory must not already exist.
    Error are reported to standard output.

    If the optional symlinks flag is true, symbolic links in the
    source tree result in symbolic links in the destination tree; if
    it is false, the contents of the files pointed to by symbolic
    links are copied.

    XXX Consider this example code rather than the ultimate tool.

    """
    names = os.listdir(src)
    os.mkdir(dst)
    for name in names:
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                copytree(srcname, dstname, symlinks)
            else:
                copy2(srcname, dstname)
            # XXX What about devices, sockets etc.?
        except (IOError, os.error), why:
            print "Can't copy %s to %s: %s" % (`srcname`, `dstname`, str(why))
Der große Harken an der ganzen Methode ist eigentlich nur "os.mkdir(dst)", denn wenn das Zielverzeichniss vorhanden ist wirft mkdir eine Exception und Schluss ist. Das ganze kann jedoch dadruch umgangen werden das man nur mkdir aufruft wenn das Verzeichniss nicht vorhanden ist. Das sollte dann in etwa so aussehen:

Code: Alles auswählen

if not os.path.isdir(dst):
    os.mkdir(dst)
So jetzt noch die if Schleife in die Funktion eingebettet und schon haben wir das gewünschte Verhalten, das die Funktion auch funktioniert, wenn das Verzeichniss schon vorhanden ist.

Die neue Funktion sieht dann foldendermaßen aus:

Code: Alles auswählen

import os
import shutil

def copytree(src, dst, symlinks=0):
    """Recursively copy a directory tree using copy2().

    The destination directory must not already exist.
    Error are reported to standard output.

    If the optional symlinks flag is true, symbolic links in the
    source tree result in symbolic links in the destination tree; if
    it is false, the contents of the files pointed to by symbolic
    links are copied.

    XXX Consider this example code rather than the ultimate tool.

    """
    names = os.listdir(src)
    if not os.path.isdir(dst):
        os.mkdir(dst)
    for name in names:
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                shutil.copytree(srcname, dstname, symlinks)
            else:
                shutil.copy2(srcname, dstname)
            # XXX What about devices, sockets etc.?
        except (IOError, os.error), why:
            print "Can't copy %s to %s: %s" % (`srcname`, `dstname`, str(why))
Wichtig: Die Funktion wurde als neue Funktion in einem eignem Modul realisiert, somit ist auch ein "shutil" vor "copytree" bzw. "copy2" nötig. Ich würde auch abraten die shutil.py direkt zu modifizieren denn das könnte zu unerwünschten Nebeneffekten führen.


Zum Schluss will ich noch meine sehr einfache Version posten, die ich benützte um nur die Dateien und nicht die Unterverzeichnisse mit zu kopieren (auch die symbolischen Links wurden aussen vorgelassen).

Code: Alles auswählen


def copyFilesFromDir(sourceDirName, targetDirName):
    try:
        names = os.listdir(sourceDirName)
        if not os.path.isdir(targetDirName):
            os.mkdir(targetDirName)
    
        for name in names:
            srcname = os.path.join(sourceDirName, name)
            dstname = os.path.join(targetDirName, name)

            shutil.copy2(srcname, dstname)
   
    except Exception, err:
        print err
Als kleine Anmerkung, eigentlich arbeite ich mit Python 2.0 da ich Jython 2.1 (?) als Skriptsprache in QF-Test 2.2.1 verwende. Somit kann das "not" eventuell gegen ein "!" ausgetauscht werden, das exception Handling in meiner Methode verbesseret werden und dirverse andere Kleinigkeiten sind auch nicht ausgeschlossen.


Getest wurde alles mit Jython 2.1 und 2.2.1.

Hoffe das wird den einem oder anderem vielleicht helfen.

Gruß
Baracke
Es geht nicht darum das Du dabei verrückt wirst.
Nein, es geht darum, daß Du Dir den Wahnsinn herbeisehnst.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hallo allerseits,

das ist eine ganze Menge Code. Aber was spricht gegen eine einfache OS-Operation wie

Code: Alles auswählen

import os
os.system("cp -R sourcedir targetdir")
Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Genau. Man muss nur sicherstellen, dass ne ordentliche Shell, nen Gnu-kompatibles `cp` vorhanden und die Namen alle richtig escaped sind.

Ganz einfach...
Baracke
User
Beiträge: 2
Registriert: Mittwoch 9. Juli 2008, 10:37

Naja sobald man Code haben muss der Betriebssystemunabhängig wird ist es nimma so einfach. Man könnte zwar abfragen welches Betriebsystem grade benutzt wird und dementsprechend andere Shell-Commandos absetzten, was jedoch dann auch nur für die Betriebssysteme gilt für die man es gemacht hat.

Die obrige Lösung hat den Vorteil das man an dem Code nichts ändern muss wenn man auch auf ein nicht vorgesehenes Betriebssystem wechselt (ausser Python hat ein anders Verhalten in den Os/Shell Modulen, was eingentlich nicht sein dürfte). Sprich einmal schreiben, nie wieder drum kümmern und gut ist.

Ausserdem ist mir sowieso nicht klar warum die "copytree" auf nicht existierende Verzeichnisse beschränkt haben, zumal es sehr simpel ist es auch für exisiterende Verzeichnisse zu modifizieren.

Gruß
Baracke

PS: Die 2te Lösung mit dem nur Dateien kopieren ist noch Fehlerhaft, es fehlt die Abfrage ob es sich um eine Datei oder ein Verzeichniss handelt. Sprich sobald die Methode versucht ein Verzeichniss zu kopieren kracht es, da copy2 nicht für Verzeichnisse funktioniert.
Es geht nicht darum das Du dabei verrückt wirst.
Nein, es geht darum, daß Du Dir den Wahnsinn herbeisehnst.
Antworten