Запустите следующий код из каталога, который содержит каталог с именем bar
(содержащий один или несколько файлов) и каталог с именем baz
(также содержащий один или несколько файлов). Убедитесь, что нет каталога с именем foo
.
import shutil
shutil.copytree('bar', 'foo')
shutil.copytree('baz', 'foo')
Это потерпит неудачу с:
$ python copytree_test.py
Traceback (most recent call last):
File "copytree_test.py", line 5, in <module>
shutil.copytree('baz', 'foo')
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/shutil.py", line 110, in copytree
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/os.py", line 172, in makedirs
OSError: [Errno 17] File exists: 'foo'
Я хочу, чтобы это работало так же, как если бы я набрал:
$ mkdir foo
$ cp bar/* foo/
$ cp baz/* foo/
Нужно ли мне использовать shutil.copy()
для копирования каждого файла в baz
в foo
? (После того, как я уже скопировал содержимое 'bar' в 'foo' shutil.copytree()
?) Или есть более простой / лучший способ?
shutil.copytree()
с Python об изменении поведения, чтобы разрешить запись в существующий каталог, но есть некоторые детали поведения, с которыми необходимо согласовать.Ответы:
Это ограничение стандарта
shutil.copytree
кажется произвольным и раздражающим. Временное решение:Обратите внимание, что это не совсем соответствует стандарту
copytree
:symlinks
иignore
параметры для корневого каталогаsrc
дерева;shutil.Error
ошибок на корневом уровнеsrc
;shutil.Error
для этого поддерева вместо того, чтобы пытаться копировать другие поддеревья и поднимать единое объединенноеshutil.Error
.источник
shutil.copytree
делаетos.makedirs(dst)
в начале. Ни одна часть кода на самом деле не будет иметь проблемы с уже существующим каталогом. Это должно быть изменено. По крайней мере, предоставьтеexist_ok=False
параметр для вызоваdef copyTree( src, dst, symlinks=False, ignore=None): for item in os.listdir(src): s = os.path.join(src, item) d = os.path.join(dst, item) if os.path.isdir(s): if os.path.isdir(d): self.recursiveCopyTree(s, d, symlinks, ignore) else: shutil.copytree(s, d, symlinks, ignore) else: shutil.copy2(s, d)
distutils.dir_util.copy_tree()
, который также находится в stdlib, не имеет такого ограничения и фактически ведет себя как ожидалось. Учитывая это, нет веских причин пытаться развернуть свою собственную ( ... обычно неработающую ) реализацию. Брендан Абель «s ответ должен абсолютно быть принятым решением в настоящее время.Вот решение, которое является частью стандартной библиотеки:
Смотрите этот похожий вопрос.
Скопируйте содержимое каталога в каталог с помощью Python
источник
distutils.errors.DistutilsInternalError: mkpath: 'name' must be a string
т.е. не принимаетPosixPath
. Надоstr(PosixPath)
. Список пожеланий для улучшения. Помимо этого вопроса, я предпочитаю этот ответ.Path
объект наследуемымstr
, как большинство предыдущих реализаций объектно-ориентированных объектов пути.distutils.dir_util.copy_tree()
считается деталью реализации distutils и не рекомендуется для публичного использования. Реальное решение должно бытьshutil.copytree()
улучшено / расширено, чтобы вести себя более похожеdistutils.dir_util.copy_tree()
, но без его недостатков. Тем временем я буду продолжать использовать пользовательские вспомогательные функции, аналогичные тем, которые представлены в других ответах.В небольшом улучшении ответ atzz на функцию, где вышеуказанная функция всегда пытается скопировать файлы из источника в место назначения.
В моей выше реализации
Я использую вышеуказанную функцию вместе со сборкой scons. Это мне очень помогло, так как каждый раз, когда я компилирую, мне не нужно копировать весь набор файлов ... а только файлы, которые были изменены.
источник
if not os.path.exists(d) or os.stat(s).st_mtime - os.stat(d).st_mtime > 1:
Слияние, вдохновленное Atzz и Mital Vora:
источник
[x for x in lst if x not in excl]
это не делает то же самое, что copytree, который использует сопоставление с шаблоном glob. en.wikipedia.org/wiki/Glob_(programming)Python 3.8 представил
dirs_exist_ok
аргумент дляshutil.copytree
:Следовательно, с Python 3.8+ это должно работать:
источник
dirs_exist_ok=False
по умолчанию в copytree, первая попытка копирования не удастся?dirs_exist_ok
упустил первый вызов, чтобы проиллюстрировать разницу (и потому что каталог еще не существует в примере OP), но, конечно, вы можете использовать его, если хотите.Документы прямо указывают, что каталог назначения не должен существовать :
Я думаю, что ваш лучший выбор для
os.walk
второго и всех последующих каталогов,copy2
каталогов и файлов и делать дополнительныеcopystat
для каталогов. В конце концов, это именно тоcopytree
, что описано в документации. Или вы можетеcopy
иcopystat
каждый каталог / файл иos.listdir
вместоos.walk
.источник
Это вдохновлено оригинальным лучшим ответом от atzz, я только что добавил логику замены файла / папки. Таким образом, он фактически не сливается, а удаляет существующий файл / папку и копирует новый:
Раскомментируйте дерево, чтобы оно стало функцией перемещения.
источник
Вот моя версия той же задачи:
источник
Вот версия, вдохновленная этой темой, которая более близко имитирует
distutils.file_util.copy_file
.updateonly
is bool, если True, будет копировать только файлы с датами изменения, более новыми, чем существующие,dst
если не указано иное,forceupdate
которые будут копироваться независимо.ignore
иforceupdate
ожидать списки имен файлов или папок / имен файлов относительноsrc
и принимать подстановочные знаки в стиле Unix, аналогичныеglob
илиfnmatch
.Функция возвращает список скопированных файлов (или будет скопирован,
dryrun
если True).источник
Предыдущее решение имеет проблему, которая
src
может быть перезаписанаdst
без какого-либо уведомления или исключения.Я добавляю
predict_error
метод для прогнозирования ошибок перед копированием.copytree
в основном на основе версии Сирила Понтье.predict_error
Лучше всего сначала использовать прогнозирование всех ошибок, если только вы не хотите, чтобы исключение возникало одна за другой при выполненииcopytree
до устранения всех ошибок.источник
Вот мой пропуск на проблему. Я изменил исходный код для copytree, чтобы сохранить исходную функциональность, но теперь ошибка не возникает, когда каталог уже существует. Я также изменил его, чтобы он не перезаписывал существующие файлы, а сохранял обе копии, одну с измененным именем, поскольку это было важно для моего приложения.
источник
Попробуй это:
источник
Вот версия, ожидающая
pathlib.Path
ввода.Обратите внимание, что для этой функции требуется Python 3.6, который является первой версией Python, в которой в
os.listdir()
качестве входных данных поддерживаются траектории. Если вам нужно поддерживать более ранние версии Python, вы можете заменитьlistdir(src)
наlistdir(str(src))
.источник
я бы предположил, что самым быстрым и простым способом было бы заставить Python вызывать системные команды ...
пример..
Tar и gzip вверх по каталогу .... распакуйте и распакуйте каталог в нужном месте.
й?
источник