Como faço para copiar um diretório inteiro de arquivos para um diretório existente usando Python?

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

  •  18-09-2019
  •  | 
  •  

Pergunta

Executar o seguinte código a partir de um diretório que contém um diretório chamado bar (contendo um ou mais arquivos) e um baz diretório chamado (também contendo um ou mais arquivos). Certifique-se de que não há um diretório chamado foo.

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

Ele vai falhar com:

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

Eu quero que isso funcione da mesma maneira como se eu tivesse digitado:

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

Eu preciso usar shutil.copy() para copiar cada arquivo em baz em foo? (Depois que eu já copiou o conteúdo do 'bar' em 'foo' com shutil.copytree()?) Ou existe uma melhor maneira mais fácil /?

Foi útil?

Solução

Esta limitação do shutil.copytree padrão parece arbitrário e irritante. Solução:

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)

Note que não é inteiramente consistente com o copytree padrão:

  • Ele não symlinks honra e ignore parâmetros para o diretório raiz da árvore src;
  • não levanta shutil.Error por erros no nível de raiz de src;
  • em caso de erros durante a cópia de uma sub-árvore, que irá aumentar shutil.Error para essa subárvore em vez de tentar copiar outros sub-árvores e levantando única shutil.Error combinado.

Outras dicas

Aqui está uma solução que é parte da biblioteca padrão.

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

Veja esta questão similar.

Copie o conteúdo do diretório em um diretório com python

Em ligeira melhoria na resposta de atzz para a função onde a função acima sempre tenta copiar os arquivos de origem para o 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)

Em minha implementação acima

  • Criar o diretório de saída, se não já existe
  • Fazer o diretório de cópia por recursivamente chamar o meu próprio método.
  • Quando chegamos a realmente copiar o arquivo eu verificar se o arquivo é modificado então somente devemos copiar.

Eu estou usando a função acima, juntamente com scons construção. Ele me ajudou muito como cada vez quando eu compilar eu não ser necessário copiar todo conjunto de arquivos .. mas apenas os arquivos que são modificados.

A um merge inspirado por atzz e 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)
  • O mesmo comportamento como shutil.copytree , com links simbólicos e ignorar parâmetros
  • Criar estrutura de destino diretório se não foram inexistentes
  • não irá falhar se dst já existe

docs declarar explicitamente que o diretório de destino deve não exist :

O diretório de destino, nomeado por dst, não deve existir; ele será criado, bem como faltando diretórios pai.

Eu acho que sua melhor aposta é a os.walk o segundo e todos os consequentes diretórios, copy2 diretório e arquivos e fazer copystat adicional para diretórios. Depois de tudo isso é precisamente o que copytree não como explicado nos docs. Ou você poderia copy e copystat cada diretório / arquivo e os.listdir vez de os.walk.

Você pode modificar shutil e obter o efeito (na minha versão de shutil este é na linha 315)

Mudança

os.makedirs(dst)

Para

os.makedirs(dst,exist_ok=True)

Esta é inspirado a partir da resposta original melhor fornecido por atzz, eu só acrescentou substituir arquivo / pasta lógica. Então, ele realmente não se fundem, mas exclui o arquivo / pasta existente e copia o novo:

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)

Remova o comentário da rmtree para torná-lo uma função movimento.

i assumiria maneira mais rápida e mais simples seria ter o Python chamar o sistema de comandos ...

exemplo ..

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

tar e gzip o diretório .... descompactação e untar o diretório no lugar desejado.

yah?

Aqui é a minha versão da mesma tarefa ::

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)

Aqui está uma versão inspirada por este segmento que mais de perto imita distutils.file_util.copy_file.

updateonly é um bool Se for verdade, só vai copiar arquivos com datas modificadas mais recentes do que os arquivos em dst existente excepto se enumeradas no forceupdate que irá copiar independentemente.

ignore e forceupdate esperar listas de nomes de arquivo ou pasta / nomes de arquivos em relação ao src e aceito Unix-style curingas semelhante ao glob ou fnmatch.

A função retorna uma lista de arquivos copiados (ou seria copiado se dryrun se True).

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

A solução anterior tem alguma questão que src poderá substituir dst sem qualquer notificação ou exceção.

eu adicionar um método predict_error prever erros antes copy.copytree de base, principalmente, na versão de Cyrille Pontvieux.

Usando predict_error de prever todos os erros no início é melhor, a menos que você gostaria de ver exceção gerada uma a outra quando executar copytree até correção todos os erros.

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)

Aqui está o meu passe para o problema. Eu modifiquei o código fonte para copytree para manter a funcionalidade original, mas agora nenhum erro ocorre quando o diretório já existe. Eu também mudou de modo que não sobrescrever arquivos existentes, mas sim mantém duas cópias, uma com um nome modificado, uma vez que esta era importante para a minha candidatura.

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)

Tente isto:

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top