Seite 1 von 1

Verzeichnis kopieren + überschreiben

Verfasst: Mittwoch 26. März 2008, 05:12
von rumpelwicht
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!

Verfasst: Mittwoch 26. März 2008, 07:49
von Jan-Peer
Die Lösung dürfte shutil.copytree() sein:

http://docs.python.org/lib/module-shutil.html

Verfasst: Mittwoch 26. März 2008, 15:09
von rumpelwicht
Leider nein, da hier vorausgesetzt wird, dass das Zielverzeichnis noch nicht existiert.

Verfasst: Mittwoch 26. März 2008, 15:14
von Hyperion
Dann nutze rmtree bevor du Kopierst ;-)

Verfasst: Mittwoch 26. März 2008, 16:32
von Jan-Peer
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.

Verfasst: Mittwoch 26. März 2008, 16:54
von rumpelwicht
Danke euch für den Tipp, werde mich also selbst an die Arbeit machen.

Verfasst: Donnerstag 27. März 2008, 17:56
von Shpongle
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 ^^

Verfasst: Mittwoch 9. Juli 2008, 11:32
von Baracke
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

Verfasst: Mittwoch 9. Juli 2008, 13:00
von Michael Schneider
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

Verfasst: Mittwoch 9. Juli 2008, 13:47
von audax
Genau. Man muss nur sicherstellen, dass ne ordentliche Shell, nen Gnu-kompatibles `cp` vorhanden und die Namen alle richtig escaped sind.

Ganz einfach...

Verfasst: Donnerstag 10. Juli 2008, 08:54
von Baracke
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.