Фильтровать каталог при использовании shutil.copytree?

#python #shutil #copytree

#python #shutil #copytree

Вопрос:

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

 shutil.copytree(directory,
                target_dir,
                ignore = shutil.ignore_patterns("/Full/Path/To/aDir/Common")) 
  

Похоже, это не работает при попытке отфильтровать «Общий» каталог, расположенный в разделе « aDir «. Если я сделаю это:

 shutil.copytree(directory,
                target_dir,
                ignore = shutil.ignore_patterns("Common"))
  

Это работает, но каждый каталог с именем Common будет отфильтрован в этом «дереве», чего я не хочу.

Есть предложения?

Спасибо.

Ответ №1:

Вы можете создать свою собственную функцию игнорирования:

 shutil.copytree('/Full/Path', 'target',
              ignore=lambda directory, contents: ['Common'] if directory == '/Full/Path/To/aDir' else [])
  

Или, если вы хотите иметь возможность вызывать copytree с относительным путем:

 import os.path
def ignorePath(path):
  def ignoref(directory, contents):
    return (f for f in contents if os.abspath(os.path.join(directory, f)) == path)
  return ignoref

shutil.copytree('Path', 'target', ignore=ignorePath('/Full/Path/To/aDir/Common'))
  

Из документов:

Если задано значение ignore , оно должно быть вызываемым, которое получит в качестве своих аргументов каталог, посещаемый copytree(), и список его содержимого, возвращаемый os.listdir() . Поскольку copytree() вызывается рекурсивно, вызываемый объект игнорирования будет вызываться один раз для каждого копируемого каталога. Вызываемый объект должен возвращать последовательность имен каталогов и файлов относительно текущего каталога (т. Е. подмножество элементов во втором аргументе); затем эти имена будут игнорироваться в процессе копирования. ignore_patterns() можно использовать для создания такого вызываемого объекта, который игнорирует имена на основе шаблонов в стиле глобуса.

Комментарии:

1. Небольшая поправка, функция ignoref должна возвращать список (т.е. [f для f в …] вместо генератора.

Ответ №2:

API для shutil.ignore_patterns() не поддерживает абсолютные пути, но тривиально легко создать свой собственный вариант.

В качестве отправной точки посмотрите на исходный код для *ignore_patterns*:

 def ignore_patterns(*patterns):
    """Function that can be used as copytree() ignore parameter.

    Patterns is a sequence of glob-style patterns
    that are used to exclude files"""
    def _ignore_patterns(path, names):
        ignored_names = []
        for pattern in patterns:
            ignored_names.extend(fnmatch.filter(names, pattern))
        return set(ignored_names)
    return _ignore_patterns
  

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

В качестве альтернативы, не используйте shutil как есть. Исходный код короткий и приятный, поэтому его нетрудно вырезать, вставлять и настраивать.

Ответ №3:

Вы захотите создать свою собственную функцию игнорирования, которая проверяет текущий обрабатываемый каталог и возвращает список, содержащий ‘Common’, только если каталог ‘/ Full / Path /To / aDir’.

 def ignore_full_path_common(dir, files):
    if dir == '/Full/Path/To/aDir':
        return ['Common']
    return []

shutil.copytree(directory, target_dir, ignore=ignore_full_path_common)
  

Ответ №4:

Большое спасибо за ответ. Это помогло мне разработать собственную ignore_patterns() функцию для немного других требований. Вставка кода здесь может кому-то помочь.

Ниже приведена ignore_patterns() функция для исключения нескольких файлов / каталогов с использованием абсолютного пути к нему.

myExclusionList —> Список, содержащий файлы / каталоги, которые должны быть исключены при копировании. Этот список может содержать шаблон подстановочных знаков. Пути в списке указаны относительно srcpath предоставленных. Например:

[СПИСОК ИСКЛЮЧЕНИЙ]

 java/app/src/main/webapp/WEB-INF/lib/test
unittests
python-buildreqs/apps/abc.tar.gz
3rd-party/jdk*
  

Код вставлен ниже

 def copydir(srcpath, dstpath, myExclusionList, log):

    patternlist = []
    try:
        # Forming the absolute path of files/directories to be excluded
        for pattern in myExclusionList:
            tmpsrcpath = join(srcpath, pattern)
            patternlist.extend(glob.glob(tmpsrcpath)) # myExclusionList can contain wildcard pattern hence glob is used
        copytree(srcpath, dstpath, ignore=ignore_patterns_override(*patternlist))
    except (IOError, os.error) as why:
        log.warning("Unable to copy %s to %s because %s", srcpath, dstpath, str(why))
        # catch the Error from the recursive copytree so that we can
        # continue with other files
    except Error as err:
        log.warning("Unable to copy %s to %s because %s", srcpath, dstpath, str(err))


# [START: Ignore Patterns]
# Modified Function to ignore patterns while copying.
# Default Python Implementation does not exclude absolute path
# given for files/directories

def ignore_patterns_override(*patterns):
    """Function that can be used as copytree() ignore parameter.
    Patterns is a sequence of glob-style patterns
    that are used to exclude files/directories"""
    def _ignore_patterns(path, names):
        ignored_names = []
        for f in names:
            for pattern in patterns:
                if os.path.abspath(join(path, f)) == pattern:
                    ignored_names.append(f)
        return set(ignored_names)
    return _ignore_patterns

# [END: Ignore Patterns]
  

Ответ №5:

Не зависит от платформы. Пути к шаблонам глобуса [«.gitkeep», «app / build», «* .txt»]

     def callbackIgnore(paths):
        """ callback for shutil.copytree """
        def ignoref(directory, contents):
            arr = [] 
            for f in contents:
                for p in paths:
                    if (pathlib.PurePath(directory, f).match(p)):
                        arr.append(f)
            return arr
    
        return ignoref