Kopiuj plik lub katalogi rekurencyjnie w Pythonie

116

Wydaje się, że Python ma funkcje do kopiowania plików (np. shutil.copy) I funkcje do kopiowania katalogów (np. shutil.copytree), Ale nie znalazłem żadnej funkcji, która obsługuje oba. Jasne, sprawdzenie, czy chcesz skopiować plik lub katalog, jest trywialne, ale wydaje się, że jest to dziwne pominięcie.

Czy naprawdę nie ma standardowej funkcji, która działa jak cp -rpolecenie unix , tj. Obsługuje zarówno katalogi, jak i pliki i kopiuje rekursywnie? Jaki byłby najbardziej elegancki sposób obejścia tego problemu w Pythonie?

pafcu
źródło
3
Tak, to jest bałagan. Jedno z miejsc, w których, próbując odzwierciedlić wywołania systemowe, Python pogarsza widoczny interfejs. Chociaż nie jest trudno przełączać się między plikiem kopii i drzewem kopii, nie powinno to być konieczne. Może zgłoś prośbę o ulepszenie w narzędziu do śledzenia błędów Pythona, aby umożliwić copytreeskopiowanie pojedynczego pliku?
bobince

Odpowiedzi:

142

Proponuję najpierw wywołać shutil.copytree, a jeśli zostanie zgłoszony wyjątek, spróbuj ponownie za pomocą 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
źródło
18
Myślę, że znacznie czystszym rozwiązaniem byłoby po prostu sprawdzenie, czy src jest katalogiem przy użyciu os.path.isdir (src) zamiast przechwytywania takiego wyjątku. A może jest jakiś szczególny powód, dla którego należy zamiast tego użyć wyjątku?
pafcu,
31
1) Ponieważ w świecie Pythona EAFP (łatwiej prosić o przebaczenie niż o pozwolenie) jest preferowane niż LBYL (patrz, zanim skoczysz). Mogę podać linki na ten temat, jeśli brzmi to dla Ciebie nowość. 2) Funkcja biblioteki już pośrednio sprawdza to, więc po co replikować sprawdzenie? 3) nic nie stoi na przeszkodzie, aby ta shutil.copytreefunkcja w przyszłości usprawniała oba przypadki i zarządzała nimi. 4) Wyjątki nie są aż tak wyjątkowe w Pythonie; np. iteracja zatrzymuje się, zgłaszając wyjątek StopIteration.
tzot
2
Cóż, w tym przypadku obsługa wyjątku zajmuje 6 linii, podczas gdy sprawdzenie typu zajmuje 4 linie. Niewiele, ale w końcu się sumuje. Ponadto, jak powiedziałeś, Copytree może pewnego dnia bardzo dobrze obsługiwać również pliki. Ale nie można powiedzieć, jak będzie wyglądać ta implementacja. Może rzuca wyjątek w pewnych okolicznościach, w których działa kopia? W takim przypadku mój kod nagle przestałby działać tylko z powodu dodanej funkcjonalności. Ale prawdopodobnie masz rację, wyjątki są dość powszechne w Pythonie, co uważam za bardzo irytujące, ale prawdopodobnie dlatego, że nigdy się do tego nie przyzwyczajam
pafcu
5
W rzeczywistości wyjątki mają jedną wyraźną obiektywną zaletę w tym przypadku: jest całkowicie możliwe (chociaż bardzo mało prawdopodobne), że typ zmienia się między sprawdzeniem a wywołaniem prawidłowej funkcji.
pafcu,
2
moim zdaniem dodawanie podstawowych funkcji w wyjątkach jest złą praktyką, nie ma znaczenia, jakiego języka używasz. umieszcza funkcjonalność w miejscu, którego wielu programistów nie będzie szukać. co więcej, jeśli nie napiszesz komentarza, mniej doświadczony programista Pythona nie zrozumie, jaki jest cel tej ponownej próby. a jeśli potrzebujesz dodać komentarz do tak trywialnej rzeczy, jak tutaj, coś w Twoim stylu kodu jest nie tak. wreszcie, napisanie if / else da znacznie łatwiejszy do odczytania kod.
this.myself
7

Aby dodać na Tzot-tych i GNS odpowiedzi, tutaj jest alternatywnym sposobem kopiowania plików i folderów rekurencyjnie. (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)

Jeśli to Twój pierwszy raz i nie masz pojęcia, jak rekurencyjnie kopiować pliki i foldery, mam nadzieję, że to pomoże.

mondieki
źródło
3

shutil.copy i shutil.copy2 kopiujesz pliki.

shutil.copytreekopiuje folder ze wszystkimi plikami i wszystkimi podfolderami. shutil.copytreejest używaneshutil.copy2 do kopiowania plików.

Więc analogiem do cp -rciebie jest to, shutil.copytreeponieważ cp -rkieruje i kopiuje folder i jego pliki / podfoldery, takie jak shutil.copytree. Bez -r cpkopii pliki lubią shutil.copyi shutil.copy2robią.

gms
źródło
1
Myślę, że nie zrozumiałeś pytania. Spróbuj shutil.copytree('C:\myfile.txt', 'C:\otherfile'). To nie działa. O to właśnie pytał OP ... 7 lat temu.
Jean-François Corbett
Oczywiście to nie działa. Podobnie jak cp nie działa z folderami. Potrzebujesz specjalnej opcji. kopiowanie i copytree to najlepszy sposób obsługi kopiowania. Gdyby copytree mogło celować i pliki, łatwo byłoby popełnić błędy. Musisz wiedzieć, do czego celujesz zarówno w Linuksie, jak iw Pythonie. To trudne. Poza tym ktoś inny skomentował to tutaj, ale widząc pytanie i odpowiedzi, nie mógł się powstrzymać przed odpowiedzią. Elegancki sposób to wiedzieć, co chcesz zrobić, a nie uniwersalna kopia bez żadnej kontroli.
gms
2

Unix cpnie „obsługuje zarówno katalogów, jak i plików”:

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

Aby cp skopiować katalog, musisz ręcznie powiedzieć cp, że jest to katalog, używając flagi „-r”.

Jest tu jednak pewne rozłączenie - cp -rpo przekazaniu nazwy pliku jako źródła z radością skopiuje tylko jeden plik; copytree nie będzie.

James Polley
źródło
2
docs.python.org/library/shutil.html zawiera kod copytree (), który demonstruje obsługę zwykłych plików, dowiązań symbolicznych i katalogów.
James Polley,
1
Ta odpowiedź nie odpowiada na pytanie. Powinien to być komentarz, a nie odpowiedź.
Jean-François Corbett
0

Myślę, że copy_tree jest tym, czego szukasz

algorytmy
źródło
-2

Metoda python shutil.copytree to bałagan. Zrobiłem taki, który działa poprawnie:

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
źródło
1
Ten kod jest dobry do pracy przy sprawdzaniu pojedynczych plików (problem z nadpisywaniem czeku), ale nie będzie działał dla plików binarnych, takich jak „zip”. Dlaczego nie użyć prostej kopii pliku Pythona zamiast odczytu / zapisu wiersz po wierszu?
notilas