Копирование и объединение каталогов, исключая определенные расширения

Я хочу скопировать несколько каталогов с одинаковой структурой (подкаталоги имеют одинаковые имена), но различное содержимое в третье место и объединить их. В то же время я хочу игнорировать некоторые расширения файлов, а не копировать их.


Я обнаружил, что первая задача может быть легко обработана функцией copy_tree() из библиотеки distutils.dir_util. Проблема здесь в том, что copy_tree() не может игнорировать файлы; он просто копирует все.

distutils.dir_util.copy_tree() - пример

dirs_to_copy = [r'J:\Data\Folder_A', r'J:\Data\Folder_B']
destination_dir = r'J:\Data\DestinationFolder'
for files in dirs_to_copy:
    distutils.dir_util.copy_tree(files, destination_dir)
    # succeeds in merging sub-directories but copies everything.
    # Due to time constrains, this is not an option.

Для второй задачи (копирование с возможностью исключения файлов) на этот раз функция copytree() из библиотеки shutil. Проблема заключается в том, что он не может объединять папки, поскольку каталог назначения не должен существовать.

shutil.copytree() - пример

dirs_to_copy = [r'J:\Data\Folder_A', r'J:\Data\Folder_B']
destination_dir = r'J:\Data\DestinationFolder'
for files in dirs_to_copy:
    shutil.copytree(files, destination_dir, ignore=shutil.ignore_patterns("*.abc"))
    # successfully ignores files with "abc" extensions but fails 
    # at the second iteration since "Destination" folder exists..

Есть ли что-то, что обеспечивает лучшее из обоих миров, или я должен сам это кодировать?

Ответ 1

Как сказал PeterBrittain, написать мою собственную версию shutil.copytree() было возможным. Ниже приведен код. Обратите внимание, что единственная разница заключается в обертке os.makedirs() в блоке if.

from shutil import copy2, copystat, Error, ignore_patterns
import os


def copytree_multi(src, dst, symlinks=False, ignore=None):
    names = os.listdir(src)
    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()

    # -------- E D I T --------
    # os.path.isdir(dst)
    if not os.path.isdir(dst):
        os.makedirs(dst)
    # -------- E D I T --------

    errors = []
    for name in names:
        if name in ignored_names:
            continue
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                copytree_multi(srcname, dstname, symlinks, ignore)
            else:
                copy2(srcname, dstname)
        except (IOError, os.error) as why:
            errors.append((srcname, dstname, str(why)))
        except Error as err:
            errors.extend(err.args[0])
    try:
        copystat(src, dst)
    except WindowsError:
        pass
    except OSError as why:
        errors.extend((src, dst, str(why)))
    if errors:
        raise Error(errors)

Ответ 2

если вы хотите напрямую использовать shutil, вот горячий патч для os.makedirs, чтобы пропустить ошибку.

import os
os_makedirs = os.makedirs
def safe_makedirs(name, mode=0777):
    if not os.path.exists(name):
        os_makedirs(name, mode)
os.makedirs = safe_makedirs

import shutil

dirs_to_copy = [r'J:\Data\Folder_A', r'J:\Data\Folder_B']
destination_dir = r'J:\Data\DestinationFolder'
if os.path.exists(destination_dir):
    shutil.rmtree(destination_dir)
for files in dirs_to_copy:
    shutil.copytree(files, destination_dir, ignore=shutil.ignore_patterns("*.abc")) code here