Execute o código a seguir em um diretório que contenha um diretório denominado bar
(contendo um ou mais arquivos) e um diretório denominado baz
(também contendo um ou mais arquivos). Verifique se não há um diretório chamado foo
.
import shutil
shutil.copytree('bar', 'foo')
shutil.copytree('baz', 'foo')
Irá 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'
Quero que isso funcione da mesma maneira como se eu tivesse digitado:
$ mkdir foo
$ cp bar/* foo/
$ cp baz/* foo/
Preciso usar shutil.copy()
para copiar cada arquivo baz
em foo
? (Depois de já ter copiado o conteúdo de 'bar' para 'foo' shutil.copytree()
?) Ou existe uma maneira mais fácil / melhor?
shutil.copytree()
do comportamento para permitir a gravação em um diretório existente, mas há alguns detalhes de comportamento que precisam ser acordados.Respostas:
Essa limitação do padrão
shutil.copytree
parece arbitrária e irritante. Gambiarra:Observe que não é totalmente consistente com o padrão
copytree
:symlinks
eignore
parâmetros para o diretório raiz dasrc
árvore;shutil.Error
erros no nível raiz desrc
;shutil.Error
para essa subárvore em vez de tentar copiar outras subárvores e aumentar uma única combinadashutil.Error
.fonte
shutil.copytree
faz umos.makedirs(dst)
no início. Nenhuma parte do código realmente teria um problema com um diretório preexistente. Isso precisa ser mudado. Pelo menos forneça umexist_ok=False
parâmetro para a chamadadef 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): if os.path.isdir(d): self.recursiveCopyTree(s, d, symlinks, ignore) else: shutil.copytree(s, d, symlinks, ignore) else: shutil.copy2(s, d)
distutils.dir_util.copy_tree()
, que também reside no stdlib, não tem essa restrição e realmente se comporta conforme o esperado. Dado isso, não há motivo convincente para tentar desenrolar sua própria implementação ( ... normalmente quebrada ). A resposta de Brendan Abel deve ser absolutamente a solução aceita agora.Aqui está uma solução que faz parte da biblioteca padrão:
Veja esta pergunta semelhante.
Copie o conteúdo do diretório para um diretório com python
fonte
distutils.errors.DistutilsInternalError: mkpath: 'name' must be a string
, ou seja, não aceitaPosixPath
. Precisastr(PosixPath)
. Lista de desejos para melhoria. Fora isso, prefiro esta resposta.Path
objeto herdamstr
Suponho que, como a maioria das implementações anteriores de orientação a objeto objetos caminho ..distutils.dir_util.copy_tree()
é considerado um detalhe de implementação do distutils e não é recomendado para uso público. A solução real deve ser parashutil.copytree()
ser aprimorada / estendida para se comportar de maneira mais semelhantedistutils.dir_util.copy_tree()
, mas sem suas deficiências. Enquanto isso, continuarei usando funções auxiliares personalizadas semelhantes às fornecidas em outras respostas.Em ligeira melhora na resposta do atzz à função em que a função acima sempre tenta copiar os arquivos da origem para o destino.
Na minha implementação acima
Estou usando a função acima, juntamente com a construção de scons. Isso me ajudou muito, pois toda vez que eu compilar, talvez não seja necessário copiar todo o conjunto de arquivos ... mas apenas os arquivos modificados.
fonte
if not os.path.exists(d) or os.stat(s).st_mtime - os.stat(d).st_mtime > 1:
Uma mesclagem inspirada em atzz e Mital Vora:
fonte
[x for x in lst if x not in excl]
isso não faz o mesmo que o copytree, que usa a correspondência de padrões globais. en.wikipedia.org/wiki/Glob_(programming)Python 3.8 introduziu o
dirs_exist_ok
argumento parashutil.copytree
:Portanto, com o Python 3.8+, isso deve funcionar:
fonte
dirs_exist_ok=False
por padrão no copytree, a primeira tentativa de cópia falhará?dirs_exist_ok
fora a primeira chamada para ilustrar a diferença (e porque o diretório ainda não existe no exemplo do OP), mas é claro que você pode usá-lo se quiser.Os documentos afirmam explicitamente que o diretório de destino não deve existir :
Eu acho que sua melhor aposta é para
os.walk
o segundo e todos os diretórios,copy2
diretórios e arquivos consequentes e fazer maiscopystat
para diretórios. Afinal, é exatamente o quecopytree
faz, conforme explicado nos documentos. Ou você poderiacopy
ecopystat
cada diretório / arquivo e, emos.listdir
vez deos.walk
.fonte
Isso é inspirado na melhor resposta original fornecida pelo atzz, acabei de adicionar a lógica de substituição de arquivos / pastas. Portanto, ele não se mescla, mas exclui o arquivo / pasta existente e copia o novo:
Remova o comentário da rmtree para torná-la uma função de movimentação.
fonte
Aqui está a minha versão da mesma tarefa:
fonte
Aqui está uma versão inspirada neste tópico que imita mais de perto
distutils.file_util.copy_file
.updateonly
é um booleano se True, copiará apenas arquivos com datas modificadas mais recentes que os arquivos existentes, adst
menos que listado naforceupdate
qual copiará independentemente.ignore
eforceupdate
espere listas de nomes de arquivos ou pastas / nomes de arquivos relativossrc
e aceite curingas no estilo Unix semelhantes aglob
oufnmatch
.A função retorna uma lista de arquivos copiados (ou seriam copiados se
dryrun
forem True).fonte
A solução anterior tem algum problema que
src
pode sobrescreverdst
sem nenhuma notificação ou exceção.Eu adiciono um
predict_error
método para prever erros antes da cópia.copytree
principalmente baseado na versão de Cyrille Pontvieux.Usar
predict_error
para prever todos os erros no início é o melhor, a menos que você queira ver a exceção gerada uma por outra ao executarcopytree
até corrigir todos os erros.fonte
Aqui está o meu passe para o problema. Modifiquei o código-fonte do copytree para manter a funcionalidade original, mas agora nenhum erro ocorre quando o diretório já existe. Também mudei para que não sobrescreva os arquivos existentes, mas mantenha as duas cópias, uma com um nome modificado, pois isso era importante para o meu aplicativo.
fonte
Tente isto:
fonte
Aqui está uma versão que espera a
pathlib.Path
como entrada.Observe que esta função requer o Python 3.6, que é a primeira versão do Python, onde
os.listdir()
suporta objetos semelhantes a caminhos como entrada. Se você precisar oferecer suporte a versões anteriores do Python, poderá substituí-lolistdir(src)
porlistdir(str(src))
.fonte
Eu assumiria maneira mais rápida e simples seria python chamar os comandos do sistema ...
exemplo..
Tar e gzip o diretório .... descompacte e descompacte o diretório no local desejado.
yah?
fonte