Копіюйте файл або каталоги рекурсивно в Python


116

Здається, Python має функції для копіювання файлів (наприклад shutil.copy) та функції копіювання каталогів (наприклад,shutil.copytree ), але я не знайшов жодної функції, яка би керувала обома. Звичайно, перевірити, чи хочете ви скопіювати файл чи каталог, банально, але це здається дивним упущенням.

Чи дійсно немає стандартної функції, яка працює як команда unix cp -r, тобто підтримує як каталоги, так і файли та копії рекурсивно? Який був би найелегантніший спосіб вирішити цю проблему в Python?


3
Так, це безлад. Одне з місць, де, намагаючись відобразити основні системні виклики, Python погіршує видимий інтерфейс. Хоча перемикатися між копіювальним файлом та деревом копій не важко, це не повинно було бути необхідним. Можливо, подайте запит на вдосконалення в трекер помилок Python, щоб дозволити copytreeскопіювати один файл?
bobince

Відповіді:


142

Я пропоную вам спершу зателефонувати shutil.copytree, і якщо виняток буде викинуто, то спробуйте знову 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

18
Я думаю, що було б набагато чіткіше просто перевірити, чи src - це каталог, що використовує os.path.isdir (src), а не ловити такий виняток. Або є якась особлива причина, а тут слід використовувати виключення?
pafcu

31
1) Тому що в світі Python EAFP (простіше просити пробачення, ніж дозволу) віддають перевагу LBYL (дивіться перед тим, як стрибнути). Я можу надати вам посилання з цього приводу, якщо це звучить для вас новим. 2) Функція бібліотеки вже побічно перевіряє це, то чому б тиражувати чек? 3) ніщо не зупиняє shutil.copytreeфункцію вдосконалювати та керувати обома справами в майбутньому. 4) Винятки не такі виняткові в Python; наприклад, ітерація припиняється, викидаючи виняток StopIteration.
tzot

2
Ну а в цьому випадку обробка виключення займає 6 рядків, а перевірка типу - 4 рядки. Не багато, але це зрештою додається. Крім того, як ви кажете, copytree може колись дуже добре підтримувати файли. Але неможливо сказати, якою буде ця реалізація. Можливо, це викидає виняток за якоїсь обставини, коли працює копія? У такому випадку мій код раптово перестане працювати саме через додану функціональність. Але ви, напевно, маєте рацію, винятки є досить комунальним в Python, що мені дуже дратує, але, мабуть, тому, що я ніколи не
звикаю

5
Насправді винятки мають в цьому випадку одну явну об'єктивну перевагу: цілком можливо (хоча й малоймовірно), що тип змінюється між чеком та викликом на правильну функцію.
pafcu

2
на мою особисту думку, додавання основних функцій за винятком випадків є поганою практикою, не має значення, якою мовою ви користуєтесь. це додає функціональність в місце, де багато розробників не шукають. далі, якщо ви не напишіть коментар, менш досвідчений розробник python насправді не зрозуміє, у чому мета цього спроби. і якщо вам потрібно додати коментар до такої дрібниці, як тут, щось у вашому стилі коду неправильно. нарешті, написання if / else призведе до набагато легшого для читання коду.
this.myself

7

Щоб додати відповіді Цота та gns , ось альтернативний спосіб рекурсивного копіювання файлів і папок. (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)

Якщо це буде ваш перший раз, і ви не знаєте, як копіювати файли та папки рекурсивно, я сподіваюся, що це допоможе.


3

shutil.copyі shutil.copy2копіюють файли.

shutil.copytreeкопіює папку з усіма файлами та всіма папками. shutil.copytreeвикористовується shutil.copy2для копіювання файлів.

Тож аналог, який cp -rви говорите, це shutil.copytreeтому, що cp -rнацілює та копіює папку та її файли / підпапки shutil.copytree. Без -r cpкопій файлів, як shutil.copyі shutil.copy2зробити.


1
Я не думаю, що ти зрозумів це питання. Спробуйте shutil.copytree('C:\myfile.txt', 'C:\otherfile'). Це не працює. Про це запитували ОП ... 7 років тому.
Жан-Франсуа Корбетт

Звичайно, це не працює. Як і cp не працює з папками. Вам потрібен спеціальний варіант. копіювати та копіювати - найкращий спосіб обробити копіювання. Якщо copytree могла націлитись на файли та файли, помилитися було б легко. Ви повинні знати, на що ви орієнтуєтесь як з Linux, так і з Python. Це важко. Плюс хтось ще прокоментував це тут, але, побачивши питання та відповіді, не втримався відповісти. Елегантний спосіб - знати, що ви хочете робити, а не універсальну копію без будь-якого контролю.
gms

2

Unix cpне підтримує і каталоги, і файли:

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

Щоб зробити cp копіюванням каталогу, ви повинні вручну сказати cp, що це каталог, використовуючи прапор '-r'.

Тут є певний відключення - cp -rколи передається ім'я файлу як джерело, з радістю буде копіювати лише один файл; copytree не буде.


2
docs.python.org/library/shutil.html включає код для copytree (), який демонструє обробку звичайних файлів, символьних посилань та каталогів.
Джеймс Поллі

1
Ця відповідь не стосується питання. Це повинен бути коментар, а не відповідь.
Жан-Франсуа Корбетт


-2

Метод пітона shutil.copytree його безлад. Я зробив один, який працює правильно:

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('..')

1
Цей код підходить для перевірки роботи окремих файлів (перевірка перезапису), але не працює для двійкових файлів, таких як "zip". Чому б не використовувати просту копію файлу python замість рядка за рядком читання / запис?
notilas
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.