¿Cómo puedo copiar un directorio completo de archivos en un directorio existente usando Python?

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

  •  18-09-2019
  •  | 
  •  

Pregunta

Ejecutar el siguiente código a partir de un directorio que contiene un directorio llamado bar (que contiene uno o más archivos) y un directorio llamado baz (también contiene uno o más archivos). Asegúrese de que no hay un directorio llamado foo.

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

fallará con:

$ 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'

Quiero que esto funcione de la misma manera como si hubiera escrito:

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

¿Es necesario utilizar shutil.copy() para copiar cada archivo en baz en foo? (Después de que ya he copiado el contenido del 'bar' en 'foo' con shutil.copytree()?) ¿O hay una manera mejor / más fácil?

¿Fue útil?

Solución

Esta limitación de la norma shutil.copytree parece arbitrario y molesto. Solución:

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)

Tenga en cuenta que no es totalmente coherente con la norma copytree:

  • no hace honor parámetros symlinks y ignore para el directorio raíz del árbol src;
  • que no plantea shutil.Error de errores en el nivel raíz de src;
  • en caso de errores durante la copia de un sub-árbol, que elevará shutil.Error para ese sub-árbol en lugar de tratar de copiar otros subárboles y elevar sola shutil.Error combinado.

Otros consejos

Aquí hay una solución que es parte de la biblioteca estándar.

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

Vea esta pregunta similar.

copia el contenido del directorio en un directorio con el pitón

En ligera mejora en la respuesta de atzz a la función donde la función anterior siempre trata de copiar los archivos del origen al destino.

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)

En mi implementación anterior

  • Crear el directorio de salida si no está ya existe
  • Hacer el directorio de la copia mediante la llamada recursiva mi propio método.
  • Cuando llegamos al hecho de copiar el archivo puedo comprobar si el archivo se modifica entonces sólo debemos copiar.

Estoy usando la función anterior, junto con scons construir. Me ayudó mucho, ya que cada vez que cuando compilo Puede que no tenga que copiar todo el conjunto de archivos .. pero sólo los archivos que son modificados.

Una fusión de uno inspirado en atzz y 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)
  • El mismo comportamiento como shutil.copytree , la enlaces simbólicos y ignorar parámetros
  • Crear una estructura de directorio de destino si no inexistente
  • no fallará si DST ya existe

documentos establecen explícitamente que el directorio de destino debe no existir :

  

El directorio de destino, nombrado por dst, no debe ya existir; se creará, así como falta directorios padre.

Creo que su mejor apuesta es os.walk el segundo y todos los directorios consiguientes, copy2 directorio y los archivos y hacer copystat adicional para los directorios. Después de todo eso es precisamente lo que no copytree como se explica en la documentación. O bien, podría copy y copystat cada directorio / archivo y os.listdir en lugar de os.walk.

Puede modificar shutil y obtener el efecto (en mi versión de este shutil está en línea 315)

Cambiar

os.makedirs(dst)

a

os.makedirs(dst,exist_ok=True)

Este es inspirada de la respuesta original mejor proporcionada por atzz, me acaba de agregar sustituir la lógica de archivos / carpetas. Por lo que en realidad no se funden, pero elimina el archivo / carpeta y copias existentes el nuevo:

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)

Elimine el rmtree para que sea una función de movimiento.

i asumiría manera más rápida y más sencilla sería tener pitón llamar ordena al sistema ...

ejemplo ..

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

tar y gzip el directorio .... descomprimir y desempaquetar el directorio en el lugar deseado.

yah?

Esta es mi versión de la misma tarea ::

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)

Aquí es una versión inspirada en este hilo que imita más de cerca distutils.file_util.copy_file.

updateonly es un bool Si es verdad, solamente se copiará los archivos con fechas de modificación más recientes que los archivos existentes en dst menos que figuren en forceupdate que copiará independientemente.

ignore y forceupdate esperan listas de nombres de archivo o carpeta de nombres de archivo / em> src y aceptar Unix al estilo de los comodines similar a glob o fnmatch.

La función devuelve una lista de archivos copiados (o sería copiado si dryrun si es verdadero).

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 solución anterior tiene algún problema que src puede sobrescribir dst sin ningún tipo de notificación o una excepción.

agrego un método para predecir predict_error errores antes copy.copytree principalmente en la versión de base de Cyrille Pontvieux.

El uso de predict_error de predecir todos los errores en un primer momento es lo mejor, a menos que te gusta ver levantó una excepción por otro cuando ejecute copytree hasta corregir todos los errores.

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)

Aquí está mi pase en el problema. He modificado el código fuente de copytree para mantener la funcionalidad original, pero ahora se produce ningún error cuando el directorio ya existe. También he cambiado por lo que no sobrescribe los archivos existentes, sino que mantiene las dos copias, una con un nombre modificado, ya que esto era importante para mi aplicación.

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)

Probar:

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")
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top