在一个目录中运行以下代码,该目录包含一个名为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?(在我已经用shutil.copytree()将'bar'的内容复制到'foo'后?)或者有更简单/更好的方法吗?


当前回答

Python 3.8向shutil.copytree引入了dirs_exist_ok参数:

递归地复制以src为根的整个目录树到名为dst的目录,并返回目标目录。Dirs_exist_ok指示在DST或任何缺失的父目录已经存在时是否引发异常。

因此,对于Python 3.8+,这应该可以工作:

import shutil

shutil.copytree('bar', 'foo')
shutil.copytree('baz', 'foo', dirs_exist_ok=True)

其他回答

这是标准书板的局限性。Copytree似乎很随意,很烦人。处理:

import os, shutil
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):
            shutil.copytree(s, d, symlinks, ignore)
        else:
            shutil.copy2(s, d)

注意,它与标准复制树并不完全一致:

它不尊重符号链接,忽略SRC树根目录的参数; 它不提高shutil。src根级别的错误; 如果在复制子树期间发生错误,它将引发shutil。错误,而不是试图复制其他子树并引发单个组合shutil.Error。

我无法编辑上面的“Boris Dalstein”答案,所以这里是这段代码的改进版本:

编辑所做的改进:

输入参数可以是str path或pathlib。路径对象。输入提示会有帮助。 如果源是一个目录,它也会创建该目录 类型是为局部变量定义的,因此IDE不会发出警告

# Recusively copies the content of the directory src to the directory dst.
# If dst doesn't exist, it is created, together with all missing parent directories.
# If a file from src already exists in dst, the file in dst is overwritten.
# Files already existing in dst which don't exist in src are preserved.
# Symlinks inside src are copied as symlinks, they are not resolved before copying.
#

def copy_dir(source: Union[str, pathlib.Path], destination: Union[str, pathlib.Path]):
    destination_path: pathlib.Path

    if isinstance(source, str):
        source_path = pathlib.Path(source)
    elif isinstance(source, pathlib.Path):
        source_path = source

    if isinstance(destination, str):
        destination_path = pathlib.Path(destination)
    elif isinstance(destination, pathlib.Path):
        destination_path = destination

    destination_path.mkdir(parents=True, exist_ok=True)
    if source_path.is_dir():
        destination_path = destination_path.joinpath(source_path.name)
        destination_path.mkdir(parents=True, exist_ok=True)

    for item in os.listdir(source_path):
        s: pathlib.Path = source_path / item
        d: pathlib.Path = destination_path / item
        if s.is_dir():
            copy_dir(s, d)
        else:
            shutil.copy2(str(s), str(d))

下面是我对相同任务的版本:

import os, glob, shutil

def make_dir(path):
    if not os.path.isdir(path):
        os.mkdir(path)


def copy_dir(source_item, destination_item):
    if os.path.isdir(source_item):
        make_dir(destination_item)
        sub_items = glob.glob(source_item + '/*')
        for sub_item in sub_items:
            copy_dir(sub_item, destination_item + '/' + sub_item.split('/')[-1])
    else:
        shutil.copy(source_item, destination_item)

Docs明确指出目标目录不应该存在:

以dst命名的目标目录必须不存在;它将被创建,同时还将创建缺失的父目录。

我觉得你最好的选择是。遍历第二个和所有后续目录、copy2目录和文件,并对目录执行额外的copyystat。毕竟,这正是copytree在文档中所做的解释。或者你可以复制和复制每个目录/文件和操作系统。而不是os.walk。

下面是一个解决方案,它是标准库的一部分:

from distutils.dir_util import copy_tree
copy_tree("/a/b/c", "/x/y/z")

看这个类似的问题。

用python将目录内容复制到目录中

参考资料- https://docs.python.org/3/distutils/apiref.html#distutils.dir_util.copy_tree