75 votes

Python - Déplacer et écraser des fichiers et des dossiers

J'ai un répertoire, 'Dst Directory', qui contient des fichiers et des dossiers et j'ai 'src Directory' qui contient également des fichiers et des dossiers. Ce que je veux faire, c'est déplacer le contenu de 'src Directory' vers 'Dst Directory' et écraser tous les fichiers qui existent avec le même nom. Ainsi, par exemple, "Répertoire src \file.txt doit être déplacé vers 'Dst Directory' et écraser le fichier .txt existant. Il en va de même pour certains dossiers, en déplaçant un dossier et en fusionnant son contenu avec le même dossier dans 'dst directory'.

J'utilise actuellement shutil.move pour déplacer le contenu de src vers dst mais il ne le fera pas si les fichiers existent déjà et il ne fusionnera pas les dossiers ; il mettra juste le dossier à l'intérieur du dossier existant.

Mise à jour : Pour rendre les choses un peu plus claires, ce que je fais, c'est dézipper une archive dans le répertoire Dst, puis y déplacer le contenu du répertoire Src et la réimprimer, ce qui a pour effet de mettre à jour les fichiers de l'archive zip. Ceci sera répété pour l'ajout de nouveaux fichiers ou de nouvelles versions de fichiers, etc, c'est pourquoi il faut écraser et fusionner.

Résolu : J'ai résolu mon problème en utilisant distutils.dir_util.copy_tree(src, dst), ceci copie les dossiers et fichiers du répertoire src vers le répertoire dst et écrase/fusionne là où c'est nécessaire. J'espère que cela aidera certaines personnes !

J'espère que cela a du sens, Merci.

0 votes

Notez que distutils.dir_util.copy_tree n'est pas en mesure de copier les fichiers spéciaux, par ex. tuyaux nommés (lancers distutils.errors.DistutilsFileError ).

64voto

Ray Vega Points 30187

Cette opération va parcourir le répertoire source, créer tous les répertoires qui n'existent pas encore dans le répertoire de destination, et déplacer les fichiers de la source vers le répertoire de destination :

import os
import shutil

root_src_dir = 'Src Directory\\'
root_dst_dir = 'Dst Directory\\'

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):
            # in case of the src and dst are the same file
            if os.path.samefile(src_file, dst_file):
                continue
            os.remove(dst_file)
        shutil.move(src_file, dst_dir)

Tout fichier préexistant sera supprimé en premier (via os.remove ) avant d'être remplacé par le fichier source correspondant. Tous les fichiers ou répertoires qui existent déjà dans la destination mais pas dans la source resteront inchangés.

3 votes

C'est bien, merci pour ça ! Je pense que c'est ce dont parlait Brandon Craig Rhodes, mais merci d'avoir fourni un extrait ! Malheureusement, vous ne pouvez pas avoir deux réponses correctes ^^

2 votes

Et pour les copier, il suffit de remplacer "shutil.move" par "shutil.copy".

1 votes

Si root_dst_dir == 'dir1 \\dir2\\ ', os.mkdir(dst_dir) enverrait une erreur disant "No such file or directory : 'dir1 \\dir2\\ '". L'utilisation de os.makedirs(dst_dir) peut éviter ce problème.

56voto

Brandon Rhodes Points 21188

Utilisez copy() à la place, qui est prêt à écraser les fichiers de destination. Si vous voulez ensuite que le premier arbre disparaisse, il suffit de rmtree() séparément une fois que vous avez fini de l'itérer.

http://docs.python.org/library/shutil.html#shutil.copy

http://docs.python.org/library/shutil.html#shutil.rmtree

Mise à jour :

Faites un os.walk() sur l'arbre source. Pour chaque répertoire, vérifiez s'il existe du côté de la destination, et os.makedirs() s'il est manquant. Pour chaque fichier, il suffit de shutil.copy() et le fichier sera créé ou écrasé, selon ce qui est approprié.

1 votes

Copy() ne peut pas copier les dossiers, n'est-ce pas ?

0 votes

Non, mais move() sur chaque fichier ne crée pas non plus les répertoires de destination, donc j'ai supposé que votre code faisait déjà un os.makedirs() sur des dossiers de destination qui n'existaient pas. Ah ! je crois que je comprends maintenant - vous faisiez un move() sur le todo arbre en même temps ? Gotchya. Je vais mettre à jour ma réponse.

0 votes

Merci pour la mise à jour, le problème est que les fichiers à copier changent constamment (ajout de nouveaux fichiers, etc.). Je devrais donc mettre à jour le code à chaque fois que j'ajoute de nouveaux fichiers à déplacer, si vous comprenez bien. Quoi qu'il en soit, je me suis débrouillé avec distutils.dir_util.copy_tree(src, dst) qui copie les dossiers et les fichiers et écrase/fusionne là où c'est nécessaire, merci pour votre aide.

9voto

ALLOY Points 1

Comme aucune des méthodes ci-dessus n'a fonctionné pour moi, j'ai écrit ma propre fonction récursive. Appelez la fonction copyTree(dir1, dir2) pour fusionner les répertoires. Fonctionne sur plusieurs plates-formes Linux et Windows.

def forceMergeFlatDir(srcDir, dstDir):
    if not os.path.exists(dstDir):
        os.makedirs(dstDir)
    for item in os.listdir(srcDir):
        srcFile = os.path.join(srcDir, item)
        dstFile = os.path.join(dstDir, item)
        forceCopyFile(srcFile, dstFile)

def forceCopyFile (sfile, dfile):
    if os.path.isfile(sfile):
        shutil.copy2(sfile, dfile)

def isAFlatDir(sDir):
    for item in os.listdir(sDir):
        sItem = os.path.join(sDir, item)
        if os.path.isdir(sItem):
            return False
    return True

def copyTree(src, dst):
    for item in os.listdir(src):
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if os.path.isfile(s):
            if not os.path.exists(dst):
                os.makedirs(dst)
            forceCopyFile(s,d)
        if os.path.isdir(s):
            isRecursive = not isAFlatDir(s)
            if isRecursive:
                copyTree(s, d)
            else:
                forceMergeFlatDir(s, d)

0 votes

Quels sont les scénarios qui n'ont pas fonctionné pour vous lorsque vous avez utilisé d'autres réponses ?

1 votes

Il faut noter que si src a un fichier avec le même nom qu'un répertoire dans dst, cette solution placera le fichier dans le répertoire qui partage son nom, alors que La solution de Ray Vega jettera OSError: [Errno 21] Is a directory .

0 votes

Cela a parfaitement fonctionné. Pas de OSError 10/12/16/21 ... J'ai eu tellement d'erreurs en essayant le sys.move(). Merci.

5voto

Vit Bernatik Points 56

Si vous avez également besoin d'écraser des fichiers avec un drapeau de lecture seule, utilisez ceci :

def copyDirTree(root_src_dir,root_dst_dir):
"""
Copy directory tree. Overwrites also read only files.
:param root_src_dir: source directory
:param root_dst_dir:  destination directory
"""
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):
            try:
                os.remove(dst_file)
            except PermissionError as exc:
                os.chmod(dst_file, stat.S_IWUSR)
                os.remove(dst_file)

        shutil.copy(src_file, dst_dir)

3voto

ArtemSBulgakov Points 524

Vous pouvez l'utiliser pour copier un répertoire en écrasant les fichiers existants :

import shutil
shutil.copytree("src", "dst", dirs_exist_ok=True)

dirs_exist_ok a été ajouté dans Python 3.8.

Voir docs : https://docs.python.org/3/library/shutil.html#shutil.copytree

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X