Copie arquivos ou diretórios recursivamente em Python

116

Python parece ter funções para copiar arquivos (por exemplo shutil.copy) e funções para copiar diretórios (por exemplo shutil.copytree), mas não encontrei nenhuma função que lide com ambos. Claro, é trivial verificar se você deseja copiar um arquivo ou diretório, mas parece uma omissão estranha.

Não existe realmente nenhuma função padrão que funcione como o cp -rcomando unix , ou seja, suporta diretórios e arquivos e cópias recursivas? Qual seria a maneira mais elegante de contornar esse problema em Python?

pafcu
fonte
3
Sim, isso é uma bagunça. Um dos lugares onde, ao tentar refletir as chamadas de sistema subjacentes, o Python piora a interface visível. Embora não seja difícil alternar entre o arquivo de cópia e a árvore de cópia, não deveria ser necessário. Talvez arquive uma solicitação de melhoria no rastreador de bug do Python para permitir copytreea cópia de um único arquivo?
bobince

Respostas:

142

Eu sugiro que você chame primeiro shutil.copytreee, se uma exceção for lançada, tente novamente com shutil.copy.

import shutil, errno

def copyanything(src, dst):
    try:
        shutil.copytree(src, dst)
    except OSError as exc: # python >2.5
        if exc.errno == errno.ENOTDIR:
            shutil.copy(src, dst)
        else: raise
tzot
fonte
18
Acho que seria muito mais limpo simplesmente verificar se src é um diretório usando os.path.isdir (src) em vez de capturar uma exceção como essa. Ou há algum motivo especial para usar uma exceção aqui?
pafcu
31
1) Porque no mundo Python, o EAFP (é mais fácil pedir perdão do que permissão) é preferível ao LBYL (olhe antes de pular). Posso fornecer links sobre isso, se parecer novo para você. 2) A função de biblioteca já verifica isso indiretamente, então por que replicar a verificação? 3) nada impede a shutil.copytreefunção de melhorar e gerenciar ambos os casos no futuro. 4) As exceções não são tão excepcionais em Python; por exemplo, uma iteração para lançando uma exceção StopIteration.
tzot
2
Bem, neste caso, lidar com a exceção leva 6 linhas, enquanto a verificação do tipo leva 4 linhas. Não muito, mas no final acaba somando. Além disso, como você disse, um dia o copytree também pode oferecer suporte a arquivos. Mas é impossível dizer como será essa implementação. Talvez lance uma exceção sob alguma circunstância em que a cópia funciona? Nesse caso, meu código para de funcionar repentinamente apenas por causa da funcionalidade adicionada. Mas você provavelmente está certo, exceções são muito comuns em Python, algo que acho muito irritante, mas provavelmente porque nunca pareço me acostumar com isso
pafcu
5
Na verdade, as exceções têm uma vantagem objetiva clara neste caso: é inteiramente possível (embora altamente improvável) que o tipo mude entre a verificação e a chamada para a função correta.
pafcu 05 de
2
na minha opinião pessoal, adicionar a funcionalidade principal em um exceto é uma prática ruim, não importa qual idioma você está usando. ele coloca a funcionalidade em um lugar onde muitos desenvolvedores não irão pesquisar. além disso, se você não escrever um comentário, um desenvolvedor de python menos experiente não entenderia realmente qual é o propósito dessa nova tentativa. e se você precisar adicionar um comentário para algo tão trivial como aqui, algo está errado no seu estilo de código. finalmente, escrever um if / else resultará em um código muito mais fácil de ler.
this.myself
7

Para adicionar respostas Tzot e gns , aqui está uma maneira alternativa de copiar arquivos e pastas recursivamente. (Python 3.X)

import os, shutil

root_src_dir = r'C:\MyMusic'    #Path/Location of the source directory
root_dst_dir = 'D:MusicBackUp'  #Path to the destination folder

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):
            os.remove(dst_file)
        shutil.copy(src_file, dst_dir)

Se for sua primeira vez e você não tiver ideia de como copiar arquivos e pastas recursivamente, espero que isso ajude.

Mondieki
fonte
3

shutil.copye shutil.copy2estão copiando arquivos.

shutil.copytreecopia uma pasta com todos os arquivos e todas as subpastas. shutil.copytreeestá usando shutil.copy2para copiar os arquivos.

Portanto, o análogo a cp -rvocê está dizendo é que o shutil.copytreeporque cp -rdireciona e copia uma pasta e seus arquivos / subpastas como shutil.copytree. Sem as -r cpcópias de arquivos como shutil.copye shutil.copy2fazer.

gms
fonte
1
Eu não acho que você entendeu a pergunta. Experimente shutil.copytree('C:\myfile.txt', 'C:\otherfile'). Não funciona. É sobre isso que o OP estava perguntando ... 7 anos atrás.
Jean-François Corbett de
Claro que não funciona. Como o cp não funciona com pastas. Você precisa de uma opção especial. copy e copytree são a melhor maneira de lidar com a cópia. Se o copytree pudesse direcionar e arquivos, seria fácil cometer erros. Você deve saber o que está almejando com Linux e Python. Tão difícil. Mais alguém comentou aqui, mas vendo a pergunta e as respostas não resisti em responder. O jeito elegante é saber o que você quer fazer e não uma cópia universal sem nenhum controle.
gms de
2

Unix cpnão 'suporta diretórios e arquivos':

betelgeuse:tmp james$ cp source/ dest/
cp: source/ is a directory (not copied).

Para fazer o cp copiar um diretório, você deve informar manualmente ao cp que é um diretório, usando a opção '-r'.

Porém, há alguma desconexão aqui - cp -rquando passado um nome de arquivo como fonte, felizmente copiaremos apenas o arquivo único; copytree não.

James Polley
fonte
2
docs.python.org/library/shutil.html inclui o código para copytree () que demonstra como lidar com arquivos comuns, links simbólicos e diretórios.
James Polley
1
Esta resposta não responde à pergunta. Deve ser um comentário, não uma resposta.
Jean-François Corbett de
0

Eu acho que copy_tree é o que você está procurando

algoritmos
fonte
-2

O método python shutil.copytree é uma bagunça. Eu fiz um que funciona corretamente:

def copydirectorykut(src, dst):
    os.chdir(dst)
    list=os.listdir(src)
    nom= src+'.txt'
    fitx= open(nom, 'w')

    for item in list:
        fitx.write("%s\n" % item)
    fitx.close()

    f = open(nom,'r')
    for line in f.readlines():
        if "." in line:
            shutil.copy(src+'/'+line[:-1],dst+'/'+line[:-1])
        else:
            if not os.path.exists(dst+'/'+line[:-1]):
                os.makedirs(dst+'/'+line[:-1])
                copydirectorykut(src+'/'+line[:-1],dst+'/'+line[:-1])
            copydirectorykut(src+'/'+line[:-1],dst+'/'+line[:-1])
    f.close()
    os.remove(nom)
    os.chdir('..')
Kutenzo
fonte
1
Este código é bom para o trabalho de verificação de arquivo individual (verificar problema de substituição), mas não funcionará para arquivos binários como 'zip'. Por que não usar uma cópia de arquivo python simples em vez de leitura / gravação linha por linha?
notilas