Как скопировать содержимое двух директорий в Python

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Для копирования фаjлов и директорий с сохранением их метаданных в уже существующую директорию хорошо подходят модули shutil и os. Ниже приведенный код демонстрирует, как это делается:

Python
Скопировать код
import os, shutil

def recursive_copy(src, dst):
    os.makedirs(dst, exist_ok=True)
    for item in os.listdir(src):
        source_item = os.path.join(src, item)
        dest_item = os.path.join(dst, item)
        if os.path.isdir(source_item):
            recursive_copy(source_item, dest_item)
        else:
            shutil.copy2(source_item, dest_item)

recursive_copy('/source/dir', '/target/existing/dir')

В результате все файлы и поддиректории из /source/dir будут перенесены в /target/existing/dir со сохранением их исходных атрибутов.

Кинга Идем в IT: пошаговый план для смены профессии

За рамками основного: исключения и версии Python

Обходной путь для версий Python ниже 3.8

Если вы используете версию Python ниже 3.8, то может возникнуть исключение OSError. Примените функцию copy_tree из модуля distutils.dir_util, чтобы избежать этой ошибки:

Python
Скопировать код
from distutils.dir_util import copy_tree

copy_tree('/source/dir', '/target/existing/dir')  # Теперь никакие OSError не помешают!

Внимательность к символическим ссылкам и правам доступа

Необходимо учесть символические ссылки и права доступа при копировании. Все должно быть скопировано правильно:

Python
Скопировать код
def custom_copy(src, dst):
    os.makedirs(dst, exist_ok=True)
    for item in os.listdir(src):
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if os.path.islink(s):
            linkto = os.readlink(s)
            os.symlink(linkto, d)
            os.lchmod(d, os.lstat(s).st_mode)
        elif os.path.isdir(s):
            custom_copy(s, d)
        else:
            shutil.copy2(s, d)
custom_copy('/source/dir', '/target/existing/dir')

Перехват ошибок на лету

Лучший способ обработать возможные ошибки — это перехватывать исключения shutil.Error:

Python
Скопировать код
try:
    custom_copy('/source/dir', '/target/existing/dir')
except shutil.Error as e:
    print(f'Ошибка: {e}')  # Python поможет определить причину ошибки

Визуализация

Предположим, что ваши папки – это полки с книгами 📚:

Вы хотите перенести свои книги (📖) на полку с мистическими сюжетами:

Python
Скопировать код
import shutil

shutil.copytree('/source/books', '/destination/mysteries', dirs_exist_ok=True)  # Магическое действие для перемещения книг

Результат операции:

Markdown
Скопировать код
До копирования: 
📚📚 Книги       📚📚 Мистика
[📖📖📖]                   [🔍🔍🔍]

После копирования: 
📚📚 Мистика
[🔍🔍🔍📖📖📖]

Таким образом, книги (📖) были перенесены на полку с мистикой (🔍)!

Различные сценарии копирования директорий

Селективное обновление: копирование только новых файлов

Если вам требуется обновить только новые или измененные файлы, условно говоря, как повара добавляют в блюдо только недостающие специи:

Python
Скопировать код
import os, shutil, filecmp

def selective_copy(src, dst):
    if not os.path.exists(dst) or not filecmp.cmp(src, dst, shallow=False):
        shutil.copy2(src, dst)

Используйте этот метод в функции recursive_copy, вместо стандартного shutil.copy2.

Копирование только поддиректорий: игнорирование файлов верхнего уровня

Если необходимо скопировать только поддиректории, исключая файлы верхнего уровня, фильтруйте их таким образом:

Python
Скопировать код
from pathlib import Path

def dirs_only_copy(src, dst):
    for item in Path(src).iterdir():
        if item.is_dir():
            recursive_copy(str(item), os.path.join(dst, item.name))
dirs_only_copy('/source/dir', '/target/existing/dir')  # Теперь копируем только папки

Сохранение метаданных состояния файлов после копирования

Для создания точной копии including содержимого с сохранением метаданных примените shutil.copystat:

Python
Скопировать код
import shutil

# После использования функции recursive_copy
shutil.copystat('/source/dir', '/target/existing/dir')  # Теперь это идеальное зеркало

Полезные материалы

  1. shutil — Файловые операции высокого уровня — Документация Python 3.12.1
  2. python – Как копировать файлы – Stack Overflow
  3. os — Разнообразные интерфейсы операционной системы — Документация Python 3.12.1
  4. pathlib — Объектно-ориентированные пути файловой системы — Документация Python 3.12.1
  5. Глоссарий — Документация Python 3.12.1
  6. Управление каталогами Python – GeeksforGeeks