在一个目录中运行以下代码,该目录包含一个名为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'后?)或者有更简单/更好的方法吗?


当前回答

以前的解决方案有一些问题,src可能会覆盖dst没有任何通知或异常。

我添加了一个predict_error方法来在复制之前预测错误。复制树主要基于西里尔庞特维欧的版本。

首先使用predict_error预测所有错误是最好的,除非您希望在执行copytree直至修复所有错误时看到一个接一个地引发异常。

def predict_error(src, dst):  
    if os.path.exists(dst):
        src_isdir = os.path.isdir(src)
        dst_isdir = os.path.isdir(dst)
        if src_isdir and dst_isdir:
            pass
        elif src_isdir and not dst_isdir:
            yield {dst:'src is dir but dst is file.'}
        elif not src_isdir and dst_isdir:
            yield {dst:'src is file but dst is dir.'}
        else:
            yield {dst:'already exists a file with same name in dst'}

    if os.path.isdir(src):
        for item in os.listdir(src):
            s = os.path.join(src, item)
            d = os.path.join(dst, item)
            for e in predict_error(s, d):
                yield e


def copytree(src, dst, symlinks=False, ignore=None, overwrite=False):
    '''
    would overwrite if src and dst are both file
    but would not use folder overwrite file, or viceverse
    '''
    if not overwrite:
        errors = list(predict_error(src, dst))
        if errors:
            raise Exception('copy would overwrite some file, error detail:%s' % errors)

    if not os.path.exists(dst):
        os.makedirs(dst)
        shutil.copystat(src, dst)
    lst = os.listdir(src)
    if ignore:
        excl = ignore(src, lst)
        lst = [x for x in lst if x not in excl]
    for item in lst:
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if symlinks and os.path.islink(s):
            if os.path.lexists(d):
                os.remove(d)
            os.symlink(os.readlink(s), d)
            try:
                st = os.lstat(s)
                mode = stat.S_IMODE(st.st_mode)
                os.lchmod(d, mode)
            except:
                pass  # lchmod not available
        elif os.path.isdir(s):
            copytree(s, d, symlinks, ignore)
        else:
            if not overwrite:
                if os.path.exists(d):
                    continue
            shutil.copy2(s, d)

其他回答

在atzz对函数的回答略有改进,上面的函数总是尝试将文件从源复制到目标。

def copytree(src, dst, symlinks=False, ignore=None):
    if not os.path.exists(dst):
        os.makedirs(dst)
    for item in os.listdir(src):
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if os.path.isdir(s):
            copytree(s, d, symlinks, ignore)
        else:
            if not os.path.exists(d) or os.stat(s).st_mtime - os.stat(d).st_mtime > 1:
                shutil.copy2(s, d)

在我上面的实现中

如果不存在,则创建输出目录 通过递归调用我自己的方法来复制目录。 当我们实际复制文件时,我检查文件是否被修改了 我们应该效仿。

我使用上面的函数以及scons构建。它帮助了我很多,因为每次当我编译时,我可能不需要复制整个文件集。但仅限于修改过的文件。

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

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

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

我认为最快最简单的方法是让python调用系统命令…

例子. .

import os
cmd = '<command line call>'
os.system(cmd)

Tar和gzip目录....将该目录解压缩并解压到所需的位置。

yah?

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

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

我无法编辑上面的“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))