Kopieren Sie Dateien oder Verzeichnisse rekursiv in Python

116

Python scheint Funktionen zum Kopieren von Dateien (z. B. shutil.copy) und Funktionen zum Kopieren von Verzeichnissen (z. B. shutil.copytree) zu haben, aber ich habe keine Funktion gefunden, die beide unterstützt. Sicher, es ist trivial zu überprüfen, ob Sie eine Datei oder ein Verzeichnis kopieren möchten, aber es scheint eine seltsame Auslassung zu sein.

Gibt es wirklich keine Standardfunktion, die wie das Unix funktioniert? cp -r Befehl , dh sowohl Verzeichnisse als auch Dateien unterstützt und rekursiv kopiert? Was wäre die eleganteste Art, dieses Problem in Python zu umgehen?

pafcu
quelle
3
Ja, das ist ein Durcheinander. Einer der Orte, an denen Python durch den Versuch, die zugrunde liegenden Systemaufrufe widerzuspiegeln, die sichtbare Oberfläche verschlechtert. Obwohl es nicht schwierig ist, zwischen Kopierdatei und Kopierbaum zu wechseln, sollte dies nicht erforderlich sein. Möglicherweise eine Erweiterungsanforderung auf dem Python-Bug-Tracker copytreeeinreichen , um das Kopieren einer einzelnen Datei zu ermöglichen?
Bobince

Antworten:

142

Ich schlage vor, Sie rufen zuerst an shutil.copytree, und wenn eine Ausnahme ausgelöst wird, versuchen Sie es erneut mit shutil.copy.

import shutil, errno

def copyanything(src, dst):
    try:
        shutil.copytree(src, dst)
    except OSError as exc: # python >2.5
        if exc.errno == errno.ENOTDIR:
            shutil.copy(src, dst)
        else: raise
tzot
quelle
17
Ich denke, es wäre viel sauberer, einfach mit os.path.isdir (src) zu überprüfen, ob src ein Verzeichnis ist, anstatt eine Ausnahme wie diese abzufangen. Oder gibt es einen besonderen Grund, warum man hier stattdessen eine Ausnahme verwenden sollte?
Pafcu
31
1) Weil in der Python-Welt EAFP (es ist einfacher, um Vergebung zu bitten als um Erlaubnis) LBYL vorgezogen wird (schauen Sie, bevor Sie springen). Ich kann Ihnen Links dazu zur Verfügung stellen, wenn es für Sie neu klingt. 2) Die Bibliotheksfunktion prüft dies bereits indirekt. Warum also die Prüfung replizieren? 3) Nichts hindert die shutil.copytreeFunktion daran, beide Fälle in Zukunft zu verbessern und zu verwalten. 4) Ausnahmen sind in Python nicht so außergewöhnlich. Beispiel: Eine Iteration wird gestoppt, indem eine StopIteration-Ausnahme ausgelöst wird.
Zot
2
In diesem Fall dauert die Behandlung der Ausnahme 6 Zeilen, während die Überprüfung des Typs 4 Zeilen dauert. Nicht viel, aber es summiert sich am Ende. Wie Sie sagen, könnte Copytree eines Tages auch Dateien unterstützen. Es ist jedoch unmöglich zu sagen, wie diese Implementierung aussehen wird. Vielleicht löst es unter bestimmten Umständen eine Ausnahme aus, wenn das Kopieren funktioniert? In diesem Fall würde mein Code plötzlich aufgrund der zusätzlichen Funktionalität nicht mehr funktionieren. Aber Sie haben wahrscheinlich Recht, Ausnahmen sind in Python ziemlich häufig, was ich sehr ärgerlich finde, aber es liegt wahrscheinlich daran, dass ich mich nie daran zu gewöhnen scheine
pafcu
5
Tatsächlich haben Ausnahmen in diesem Fall einen klaren objektiven Vorteil: Es ist durchaus möglich (obwohl höchst unwahrscheinlich), dass sich der Typ zwischen der Prüfung und dem Aufruf der richtigen Funktion ändert.
Pafcu
2
Meiner persönlichen Meinung nach ist das Hinzufügen von Kernfunktionen in einer Ausnahme eine schlechte Praxis, unabhängig davon, welche Sprache Sie verwenden. Es bringt Funktionalität an einen Ort, an dem viele Entwickler nicht suchen. Wenn Sie keinen Kommentar schreiben, würde ein weniger erfahrener Python-Entwickler den Zweck dieses erneuten Versuchs nicht wirklich verstehen. und wenn Sie einen Kommentar für eine so triviale Sache wie hier hinzufügen müssen, stimmt etwas an Ihrem Codestil nicht. Schließlich führt das Schreiben eines if / else zu einem viel einfacher zu lesenden Code.
this.myself
7

Um die Antworten von Tzot und gns hinzuzufügen , haben Sie eine alternative Möglichkeit, Dateien und Ordner rekursiv zu kopieren. (Python 3.X)

import os, shutil

root_src_dir = r'C:\MyMusic'    #Path/Location of the source directory
root_dst_dir = 'D:MusicBackUp'  #Path to the destination folder

for src_dir, dirs, files in os.walk(root_src_dir):
    dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    for file_ in files:
        src_file = os.path.join(src_dir, file_)
        dst_file = os.path.join(dst_dir, file_)
        if os.path.exists(dst_file):
            os.remove(dst_file)
        shutil.copy(src_file, dst_dir)

Sollte es Ihr erstes Mal sein und Sie keine Ahnung haben, wie Sie Dateien und Ordner rekursiv kopieren können, hoffe ich, dass dies hilft.

mondieki
quelle
3

shutil.copyund shutil.copy2werden das Kopieren von Dateien.

shutil.copytreekopiert einen Ordner mit allen Dateien und allen Unterordnern. shutil.copytreebenutztshutil.copy2 um die Dateien zu kopieren.

So ist der Analog cp -rSie sagen das ist , shutil.copytreeweil cp -rZiele und kopiert einen Ordner und seine Dateien / Unterordner möchten shutil.copytree. Ohne die -r cpKopien mögen Dateien shutil.copyund shutil.copy2tun.

g
quelle
Ich glaube nicht, dass Sie die Frage verstanden haben. Versuchen Sie es shutil.copytree('C:\myfile.txt', 'C:\otherfile'). Es funktioniert nicht. Darum hat das OP vor 7 Jahren gebeten.
Jean-François Corbett
Natürlich funktioniert es nicht. Als ob cp nicht mit Ordnern funktioniert. Sie benötigen eine spezielle Option. Kopieren und Kopieren sind der beste Weg, um das Kopieren zu handhaben. Wenn Copytree auf Dateien abzielen könnte, wäre es leicht, Fehler zu machen. Sie müssen wissen, auf was Sie sowohl mit Linux als auch mit Python abzielen. So schwer. Außerdem hat es hier jemand anderes kommentiert, aber die Frage und die Antworten zu sehen, konnte nicht widerstehen, sie zu beantworten. Der elegante Weg ist, zu wissen, was Sie tun möchten, und keine universelle Kopie ohne Kontrolle.
gms
2

Unix cpunterstützt nicht sowohl Verzeichnisse als auch Dateien:

betelgeuse:tmp james$ cp source/ dest/
cp: source/ is a directory (not copied).

Damit cp ein Verzeichnis kopiert, müssen Sie cp mithilfe des Flags '-r' manuell mitteilen, dass es sich um ein Verzeichnis handelt.

Hier gibt es jedoch einige Unterbrechungen - cp -rwenn ein Dateiname übergeben wird, kopiert die Quelle nur die einzelne Datei. Copytree wird nicht.

James Polley
quelle
2
docs.python.org/library/shutil.html enthält den Code für copytree (), der den Umgang mit normalen Dateien, Symlinks und Verzeichnissen demonstriert.
James Polley
1
Diese Antwort geht nicht auf die Frage ein. Es sollte ein Kommentar sein, keine Antwort.
Jean-François Corbett
0

Ich denke, copy_tree ist das, wonach Sie suchen

Algorithmen
quelle
-2

Die Python Shutil.copytree Methode ist ein Chaos. Ich habe eine gemacht, die richtig funktioniert:

def copydirectorykut(src, dst):
    os.chdir(dst)
    list=os.listdir(src)
    nom= src+'.txt'
    fitx= open(nom, 'w')

    for item in list:
        fitx.write("%s\n" % item)
    fitx.close()

    f = open(nom,'r')
    for line in f.readlines():
        if "." in line:
            shutil.copy(src+'/'+line[:-1],dst+'/'+line[:-1])
        else:
            if not os.path.exists(dst+'/'+line[:-1]):
                os.makedirs(dst+'/'+line[:-1])
                copydirectorykut(src+'/'+line[:-1],dst+'/'+line[:-1])
            copydirectorykut(src+'/'+line[:-1],dst+'/'+line[:-1])
    f.close()
    os.remove(nom)
    os.chdir('..')
Kutenzo
quelle
1
Dieser Code eignet sich gut für die Prüfung einzelner Dateien (Problem beim Überschreiben), funktioniert jedoch nicht für Binärdateien wie 'zip'. Warum nicht eine einfache Python-Dateikopie anstelle von zeilenweisem Lesen / Schreiben verwenden?
Notilas