Comment puis-je copier un répertoire entier de fichiers dans un répertoire existant en utilisant Python?

StackOverflow https://stackoverflow.com/questions/1868714

  •  18-09-2019
  •  | 
  •  

Question

Exécuter le code suivant à partir d'un répertoire qui contient un répertoire nommé bar (contenant un ou plusieurs fichiers) et un répertoire nommé baz (contenant également un ou plusieurs fichiers). Assurez-vous qu'il n'y a pas un répertoire nommé foo.

import shutil
shutil.copytree('bar', 'foo')
shutil.copytree('baz', 'foo')

Il échouera avec:

$ python copytree_test.py 
Traceback (most recent call last):
  File "copytree_test.py", line 5, in <module>
    shutil.copytree('baz', 'foo')
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/shutil.py", line 110, in copytree
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/os.py", line 172, in makedirs
OSError: [Errno 17] File exists: 'foo'

Je veux que cela fonctionne de la même manière que si j'avais tapé:

$ mkdir foo
$ cp bar/* foo/
$ cp baz/* foo/

Ai-je besoin d'utiliser shutil.copy() pour copier chaque fichier dans baz dans foo? (Une fois que je l'ai déjà copié le contenu de « bar » dans « foo » avec shutil.copytree()?) Ou est-il un moyen plus facile / mieux?

Était-ce utile?

La solution

Cette limitation de la shutil.copytree norme semble arbitraire et ennuyeux. Solution:

def copytree(src, dst, symlinks=False, ignore=None):
    for item in os.listdir(src):
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if os.path.isdir(s):
            shutil.copytree(s, d, symlinks, ignore)
        else:
            shutil.copy2(s, d)

Notez que ce n'est pas tout à fait conforme à la norme copytree:

  • il ne respecte pas les paramètres de symlinks et ignore pour le répertoire racine de l'arbre src;
  • il ne soulève pas shutil.Error des erreurs au niveau de la racine de src;
  • en cas d'erreurs lors de la copie d'un sous-arbre, il soulèvera shutil.Error pour cette sous-arborescence au lieu d'essayer de copier d'autres sous-arbres et d'élever shutil.Error CONFONDUS.

Autres conseils

Voici une solution qui fait partie de la bibliothèque standard.

from distutils.dir_util import copy_tree
copy_tree("/a/b/c", "/x/y/z")

Voir cette question similaire.

Copier le contenu du répertoire dans un répertoire avec python

En légère amélioration sur la réponse de atzz à la fonction où la fonction ci-dessus essaie toujours de copier les fichiers de la source à la destination.

def copytree(src, dst, symlinks=False, ignore=None):
    if not os.path.exists(dst):
        os.makedirs(dst)
    for item in os.listdir(src):
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if os.path.isdir(s):
            copytree(s, d, symlinks, ignore)
        else:
            if not os.path.exists(d) or os.stat(s).st_mtime - os.stat(d).st_mtime > 1:
                shutil.copy2(s, d)

Dans ma mise en œuvre ci-dessus

  • Création du répertoire de sortie si existe pas déjà
  • Faire le répertoire de copie en appelant récursive ma propre méthode.
  • Lorsque nous arrivons à la copie en fait le fichier que je vérifie si le fichier est modifié alors seulement nous devrions copier.

J'utilise ci-dessus fonctionnent avec construire scons. Il m'a beaucoup aidé comme à chaque fois que je compile je ne peux pas besoin de copier ensemble des fichiers .. mais uniquement les fichiers qui sont modifiés.

A fusionner une inspiré par atzz et Mital Vora:

#!/usr/bin/python
import os
import shutil
import stat
def copytree(src, dst, symlinks = False, ignore = None):
  if not os.path.exists(dst):
    os.makedirs(dst)
    shutil.copystat(src, dst)
  lst = os.listdir(src)
  if ignore:
    excl = ignore(src, lst)
    lst = [x for x in lst if x not in excl]
  for item in lst:
    s = os.path.join(src, item)
    d = os.path.join(dst, item)
    if symlinks and os.path.islink(s):
      if os.path.lexists(d):
        os.remove(d)
      os.symlink(os.readlink(s), d)
      try:
        st = os.lstat(s)
        mode = stat.S_IMODE(st.st_mode)
        os.lchmod(d, mode)
      except:
        pass # lchmod not available
    elif os.path.isdir(s):
      copytree(s, d, symlinks, ignore)
    else:
      shutil.copy2(s, d)
  • Même comportement que shutil.copytree , liens symboliques et Ignorer Paramètres
  • Création d'une structure de répertoire destination si inexistant
  • ne manquera pas si dst existe déjà

docs indiquent explicitement que le répertoire de destination doit pas existe :

  

Le répertoire de destination, nommé par dst, ne doit pas exister déjà; il sera créé ainsi que les répertoires parents manquants.

Je pense que le mieux est de os.walk le second et tous les répertoires qui en découlent, copy2 répertoire et les fichiers et faire copystat supplémentaires pour les répertoires. Après tout ce qui est précisément ce que copytree ne, comme expliqué dans la documentation. Ou vous pouvez copy et copystat chaque répertoire / fichier et os.listdir au lieu de os.walk.

Vous pouvez modifier shutil et obtenir l'effet (sur ma version de ce shutil est en ligne 315)

Modifier

os.makedirs(dst)

Pour

os.makedirs(dst,exist_ok=True)

Ceci est inspiré de la meilleure réponse originale fournie par atzz, je viens d'ajouter remplacer le fichier / logique de dossier. Donc, il ne fusionne pas réellement, mais supprime le fichier existant / dossier et copie le nouveau:

import shutil
import os
def copytree(src, dst, symlinks=False, ignore=None):
    for item in os.listdir(src):
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if os.path.exists(d):
            try:
                shutil.rmtree(d)
            except Exception as e:
                print e
                os.unlink(d)
        if os.path.isdir(s):
            shutil.copytree(s, d, symlinks, ignore)
        else:
            shutil.copy2(s, d)
    #shutil.rmtree(src)

Décommentez la rmtree pour en faire une fonction de déplacement.

Je suppose que la plus rapide et la plus simple serait Python est appelé le système des commandes ...

exemple ..

import os
cmd = '<command line call>'
os.system(cmd)

Tar et Gzip le répertoire .... décompressez et décompressez le répertoire à l'endroit désiré.

yah?

Voici ma version de la même tâche ::

import os, glob, shutil

def make_dir(path):
    if not os.path.isdir(path):
        os.mkdir(path)


def copy_dir(source_item, destination_item):
    if os.path.isdir(source_item):
        make_dir(destination_item)
        sub_items = glob.glob(source_item + '/*')
        for sub_item in sub_items:
            copy_dir(sub_item, destination_item + '/' + sub_item.split('/')[-1])
    else:
        shutil.copy(source_item, destination_item)

Voici une version inspirée de ce fil qui imite de plus près distutils.file_util.copy_file.

updateonly est un bool si vrai, ne fera que copier des fichiers avec des dates modifiées plus récents que les fichiers existants dans dst moins qu'ils ne figurent dans forceupdate qui copiera indépendamment.

ignore et forceupdate attendent des listes de noms de fichiers ou dossier / em> src et accepter de style Unix caractères génériques similaire à glob ou fnmatch.

La fonction retourne une liste de fichiers copiés (ou serait copié si dryrun si vrai).

import os
import shutil
import fnmatch
import stat
import itertools

def copyToDir(src, dst, updateonly=True, symlinks=True, ignore=None, forceupdate=None, dryrun=False):

    def copySymLink(srclink, destlink):
        if os.path.lexists(destlink):
            os.remove(destlink)
        os.symlink(os.readlink(srclink), destlink)
        try:
            st = os.lstat(srclink)
            mode = stat.S_IMODE(st.st_mode)
            os.lchmod(destlink, mode)
        except OSError:
            pass  # lchmod not available
    fc = []
    if not os.path.exists(dst) and not dryrun:
        os.makedirs(dst)
        shutil.copystat(src, dst)
    if ignore is not None:
        ignorepatterns = [os.path.join(src, *x.split('/')) for x in ignore]
    else:
        ignorepatterns = []
    if forceupdate is not None:
        forceupdatepatterns = [os.path.join(src, *x.split('/')) for x in forceupdate]
    else:
        forceupdatepatterns = []
    srclen = len(src)
    for root, dirs, files in os.walk(src):
        fullsrcfiles = [os.path.join(root, x) for x in files]
        t = root[srclen+1:]
        dstroot = os.path.join(dst, t)
        fulldstfiles = [os.path.join(dstroot, x) for x in files]
        excludefiles = list(itertools.chain.from_iterable([fnmatch.filter(fullsrcfiles, pattern) for pattern in ignorepatterns]))
        forceupdatefiles = list(itertools.chain.from_iterable([fnmatch.filter(fullsrcfiles, pattern) for pattern in forceupdatepatterns]))
        for directory in dirs:
            fullsrcdir = os.path.join(src, directory)
            fulldstdir = os.path.join(dstroot, directory)
            if os.path.islink(fullsrcdir):
                if symlinks and dryrun is False:
                    copySymLink(fullsrcdir, fulldstdir)
            else:
                if not os.path.exists(directory) and dryrun is False:
                    os.makedirs(os.path.join(dst, dir))
                    shutil.copystat(src, dst)
        for s,d in zip(fullsrcfiles, fulldstfiles):
            if s not in excludefiles:
                if updateonly:
                    go = False
                    if os.path.isfile(d):
                        srcdate = os.stat(s).st_mtime
                        dstdate = os.stat(d).st_mtime
                        if srcdate > dstdate:
                            go = True
                    else:
                        go = True
                    if s in forceupdatefiles:
                        go = True
                    if go is True:
                        fc.append(d)
                        if not dryrun:
                            if os.path.islink(s) and symlinks is True:
                                copySymLink(s, d)
                            else:
                                shutil.copy2(s, d)
                else:
                    fc.append(d)
                    if not dryrun:
                        if os.path.islink(s) and symlinks is True:
                            copySymLink(s, d)
                        else:
                            shutil.copy2(s, d)
    return fc

La solution précédente a une question qui src peut remplacer dst sans notification ou exception.

ajouter une méthode predict_error pour prédire des erreurs avant copy.copytree la base principalement sur la version de Cyrille Pontvieux.

Utilisation predict_error de prévoir toutes les erreurs dans un premier temps est le meilleur, à moins que vous voulez voir exception soulevée par une autre quand exécuter copytree jusqu'à corriger toute erreur.

def predict_error(src, dst):  
    if os.path.exists(dst):
        src_isdir = os.path.isdir(src)
        dst_isdir = os.path.isdir(dst)
        if src_isdir and dst_isdir:
            pass
        elif src_isdir and not dst_isdir:
            yield {dst:'src is dir but dst is file.'}
        elif not src_isdir and dst_isdir:
            yield {dst:'src is file but dst is dir.'}
        else:
            yield {dst:'already exists a file with same name in dst'}

    if os.path.isdir(src):
        for item in os.listdir(src):
            s = os.path.join(src, item)
            d = os.path.join(dst, item)
            for e in predict_error(s, d):
                yield e


def copytree(src, dst, symlinks=False, ignore=None, overwrite=False):
    '''
    would overwrite if src and dst are both file
    but would not use folder overwrite file, or viceverse
    '''
    if not overwrite:
        errors = list(predict_error(src, dst))
        if errors:
            raise Exception('copy would overwrite some file, error detail:%s' % errors)

    if not os.path.exists(dst):
        os.makedirs(dst)
        shutil.copystat(src, dst)
    lst = os.listdir(src)
    if ignore:
        excl = ignore(src, lst)
        lst = [x for x in lst if x not in excl]
    for item in lst:
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if symlinks and os.path.islink(s):
            if os.path.lexists(d):
                os.remove(d)
            os.symlink(os.readlink(s), d)
            try:
                st = os.lstat(s)
                mode = stat.S_IMODE(st.st_mode)
                os.lchmod(d, mode)
            except:
                pass  # lchmod not available
        elif os.path.isdir(s):
            copytree(s, d, symlinks, ignore)
        else:
            if not overwrite:
                if os.path.exists(d):
                    continue
            shutil.copy2(s, d)

Voici mon passe au problème. J'ai modifié le code source pour copytree pour maintenir la fonctionnalité originale, mais maintenant aucune erreur se produit lorsque le répertoire existe déjà. J'ai aussi changé de sorte qu'il ne remplace pas les fichiers existants mais conserve plutôt les deux exemplaires, l'un avec un nom modifié, étant donné que cela était important pour mon application.

import shutil
import os


def _copytree(src, dst, symlinks=False, ignore=None):
    """
    This is an improved version of shutil.copytree which allows writing to
    existing folders and does not overwrite existing files but instead appends
    a ~1 to the file name and adds it to the destination path.
    """

    names = os.listdir(src)
    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()

    if not os.path.exists(dst):
        os.makedirs(dst)
        shutil.copystat(src, dst)
    errors = []
    for name in names:
        if name in ignored_names:
            continue
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        i = 1
        while os.path.exists(dstname) and not os.path.isdir(dstname):
            parts = name.split('.')
            file_name = ''
            file_extension = parts[-1]
            # make a new file name inserting ~1 between name and extension
            for j in range(len(parts)-1):
                file_name += parts[j]
                if j < len(parts)-2:
                    file_name += '.'
            suffix = file_name + '~' + str(i) + '.' + file_extension
            dstname = os.path.join(dst, suffix)
            i+=1
        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, ignore)
            else:
                shutil.copy2(srcname, dstname)
        except (IOError, os.error) as why:
            errors.append((srcname, dstname, str(why)))
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except BaseException as err:
            errors.extend(err.args[0])
    try:
        shutil.copystat(src, dst)
    except WindowsError:
        # can't copy file access times on Windows
        pass
    except OSError as why:
        errors.extend((src, dst, str(why)))
    if errors:
        raise BaseException(errors)

Essayez ceci:

import os,shutil

def copydir(src, dst):
  h = os.getcwd()
  src = r"{}".format(src)
  if not os.path.isdir(dst):
     print("\n[!] No Such directory: ["+dst+"] !!!")
     exit(1)

  if not os.path.isdir(src):
     print("\n[!] No Such directory: ["+src+"] !!!")
     exit(1)
  if "\\" in src:
     c = "\\"
     tsrc = src.split("\\")[-1:][0]
  else:
    c = "/"
    tsrc = src.split("/")[-1:][0]

  os.chdir(dst)
  if os.path.isdir(tsrc):
    print("\n[!] The Directory Is already exists !!!")
    exit(1)
  try:
    os.mkdir(tsrc)
  except WindowsError:
    print("\n[!] Error: In[ {} ]\nPlease Check Your Dirctory Path !!!".format(src))
    exit(1)
  os.chdir(h)
  files = []
  for i in os.listdir(src):
    files.append(src+c+i)
  if len(files) > 0:
    for i in files:
        if not os.path.isdir(i):
            shutil.copy2(i, dst+c+tsrc)

  print("\n[*] Done ! :)")

copydir("c:\folder1", "c:\folder2")
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top