(distutil + shutil) * copytree =?

8 投票
4 回答
5032 浏览
提问于 2025-04-17 03:02

我想要使用shutil库的copytree函数中的忽略模式。同时,我希望源文件夹中的所有文件和文件夹都能替换目标目录中已有的文件和文件夹,就像distutil.dir_util.copy_tree那样。

我还是个比较新的Python用户,似乎找不到相关的帖子。

4 个回答

1

这是一个同时使用distutils和shutil的解决办法:

ignorePatterns=('.git')
shutil.copytree(src, "__TMP__", ignore=shutil.ignore_patterns(ignorePatterns))
distutils.dir_util.copy_tree("__TMP__", dest)
distutils.dir_util.remove_tree("__TMP__")

注意:这种方法效率不高,因为它会把相同的文件复制两次。

1

我只是对Dan的回答进行了扩展,加入了忽略参数,并在'Error'类中添加了shutil(用于copytree):

def copytree(src, dst, symlinks=False, ignore=None):
    """Recursively copy a directory tree using copy2().

    The destination directory must not already exist.
    If exception(s) occur, an Error is raised with a list of reasons.

    If the optional symlinks flag is true, symbolic links in the
    source tree result in symbolic links in the destination tree; if
    it is false, the contents of the files pointed to by symbolic
    links are copied.

    XXX Consider this example code rather than the ultimate tool.

    """
    names = os.listdir(src)
    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()

    _mkdir(dst) # XXX
    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(srcname, dstname, symlinks, ignore)
            else:
                shutil.copy2(srcname, dstname)
            # XXX What about devices, sockets etc.?
        except (IOError, os.error), why:
            errors.append((srcname, dstname, str(why)))
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except shutil.Error, err:
            errors.extend(err.args[0])
    try:
        shutil.copystat(src, dst)
    except WindowsError:
        # can't copy file access times on Windows
        pass
3

我本来想早点说这个,但没说。http://docs.python.org/library/shutil.html 上有一个叫做 copytree 的函数。我查看了一下,想知道它是否会替换已有的文件。从我了解到的情况来看,它会覆盖已有的文件,但如果任何一个目录已经存在,它就会失败。这是因为 os.mkdirs 在目录已存在时会出错。

需要导入的内容:

import os
import os.path
import shutil

这里用到了 _mkdir,这个可以从 http://code.activestate.com/recipes/82465-a-friendly-mkdir/ 找到(那里的一个评论提到 os.mkdirs 的行为大部分是一样的,但没有注意到 _mkdir 在要创建的目录已经存在时不会出错)。

def _mkdir(newdir):
    """works the way a good mkdir should :)
        - already exists, silently complete
        - regular file in the way, raise an exception
        - parent directory(ies) does not exist, make them as well
    """
    if os.path.isdir(newdir):
        pass
    elif os.path.isfile(newdir):
        raise OSError("a file with the same name as the desired " \
                      "dir, '%s', already exists." % newdir)
    else:
        head, tail = os.path.split(newdir)
        if head and not os.path.isdir(head):
            _mkdir(head)
        #print "_mkdir %s" % repr(newdir)
        if tail:
            os.mkdir(newdir)

虽然 _mkdir 不像 os.mkdirs 那样接受 mode 参数,但 copytree 不用这个参数,所以其实不需要。

然后把 copytree 改成调用 _mkdir 而不是 os.mkdirs

def copytree(src, dst, symlinks=False):
    """Recursively copy a directory tree using copy2().

    The destination directory must not already exist.
    If exception(s) occur, an Error is raised with a list of reasons.

    If the optional symlinks flag is true, symbolic links in the
    source tree result in symbolic links in the destination tree; if
    it is false, the contents of the files pointed to by symbolic
    links are copied.

    XXX Consider this example code rather than the ultimate tool.

    """
    names = os.listdir(src)
    # os.makedirs(dst)
    _mkdir(dst) # XXX
    errors = []
    for name in names:
        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(srcname, dstname, symlinks)
            else:
                shutil.copy2(srcname, dstname)
            # XXX What about devices, sockets etc.?
        except (IOError, os.error), why:
            errors.append((srcname, dstname, str(why)))
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except Error, err:
            errors.extend(err.args[0])
    try:
        shutil.copystat(src, dst)
    except WindowsError:
        # can't copy file access times on Windows
        pass

撰写回答