如何在python中递归复制目录并覆盖所有内容?


70

我正在尝试将/home/myUser/dir1/其所有内容(及其内容等)复制到/home/myuser/dir2/python中。此外,我希望副本覆盖中的所有内容dir2/

看起来distutils.dir_util.copy_tree可能是这个职位的合适的工具,但不能肯定是否有什么更容易/更明显,使用这样一个简单的任务。

如果它是正确的工具,该如何使用?根据文档,它需要8个参数。我是否必须通过所有8个just srcdstupdate,如果是,则通过(我是Python的新手)。

如果有更好的东西,有人可以给我一个例子,指出正确的方向吗?提前致谢!


9
os.system("cp -rf /src/dir /dest/dir")会很容易...
Joran Beasley

感谢@JoranBeasley(+1)-但是,根据cp的文档,-farg(“ force”):如果无法打开现有的目标文件,请将其删除并重...这似乎并不相同作为“全部覆盖”。您可以确认它是相同的,并且无论dir1所有内容都被(累加地)复制到dir2的子树中吗?再次感谢!
IAmYourFaja 2012年

尝试一下...它应该可以正常工作:)我从来没有遇到过问题...
Joran Beasley 2012年

4
@ 4herpsand7derpsago:cp默认情况下会覆盖文件。有一个开关可以防止它覆盖文件,但是不能反过来。
Blender 2012年

2
@JoranBeasley该方法平台独立吗?
HelloGoodbye

Answers:


71

您可以使用distutils.dir_util.copy_tree。它工作得很好,你不必通过每次吵架,只有srcdst是强制性的。

但是,根据您的情况,您不能使用类似的工具,shutil.copytree因为它的行为有所不同:由于目标目录一定不存在,因此该函数不能用于覆盖其内容。

如果要cp按照问题注释中的建议使用该工具,请注意,使用os.system函数subprocess文档中可以看到,当前建议使用模块来生成新进程。


1
谢谢!请注意,我需要两次导入才能使其正常工作。见stackoverflow.com/questions/18908941/...
kmarsh

2
shutil和之间的逻辑/哲学区别是distutils.dir_util什么?一般来说,一个比另一个更好吗?
Mike Ottum

1
distutils.dir_util.copy_tree不将覆盖退出文件
伊莱Borodach

1
另外,请注意,如果使用相同的参数两次调用此方法,则在清理目标目录时将失败:bugs.python.org/issue22132
JP

1
distutils不赞成使用,还是仅赞成setuptoolsin setup.py
安德烈亚斯(Andreas)厌倦审查制度,

35

查看shutil包装,尤其是rmtreecopytree。您可以使用来检查文件/路径是否存在os.paths.exists(<path>)

import shutil
import os

def copy_and_overwrite(from_path, to_path):
    if os.path.exists(to_path):
        shutil.rmtree(to_path)
    shutil.copytree(from_path, to_path)

copytree如果Dirs已经存在,Vincent就不工作是正确的。distutils更好的版本也是如此。以下是的固定版本shutil.copytree。它基本上是1-1复制的,除了第一个os.makedirs()放在if-else-construct之后:

import os
from shutil import *
def copytree(src, dst, symlinks=False, ignore=None):
    names = os.listdir(src)
    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()

    if not os.path.isdir(dst): # This one line does the trick
        os.makedirs(dst)
    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:
                # Will raise a SpecialFileError for unsupported file types
                copy2(srcname, dstname)
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except Error, err:
            errors.extend(err.args[0])
        except EnvironmentError, why:
            errors.append((srcname, dstname, str(why)))
    try:
        copystat(src, dst)
    except OSError, why:
        if WindowsError is not None and isinstance(why, WindowsError):
            # Copying file access times may fail on Windows
            pass
        else:
            errors.extend((src, dst, str(why)))
    if errors:
        raise Error, errors

1
您的示例不会覆盖任何内容,只需将一个目录替换为另一个目录即可。据我所知,替换内容和覆盖内容是不同的事情。该问题专门要求覆盖。
Vicent

好吧,我想这取决于您对覆盖的定义。是否要保留或不保留目标文件夹中的非重复文件。这个版本没有保留任何内容,是的。
2012年

我只是看看的代码copytree。因此,如果您只想像@Vincent所提到的那样简单地覆盖,则只需使用shutil.copytree()就可以了。当前文件将被自动覆盖。
2012年

2
shutil.copytree不应该工作;如果是这样,那就是一个错误,因为它的文档说“由dst命名的目标目录必须不存在”。
Fred Foo 2012年

28

这是一个简单的解决方案,以递归方式用源覆盖目标,并在运行时创建任何必要的目录。这不处理符号链接,但这将是一个简单的扩展(请参见上面@Michael的回答)。

def recursive_overwrite(src, dest, ignore=None):
    if os.path.isdir(src):
        if not os.path.isdir(dest):
            os.makedirs(dest)
        files = os.listdir(src)
        if ignore is not None:
            ignored = ignore(src, files)
        else:
            ignored = set()
        for f in files:
            if f not in ignored:
                recursive_overwrite(os.path.join(src, f), 
                                    os.path.join(dest, f), 
                                    ignore)
    else:
        shutil.copyfile(src, dest)

谢谢,这对我有用。我喜欢它不会删除现有文件的事实。我必须添加一点,以确保else:零件中文件的目标目录存在。
Batandwa 2014年

2
@mgrant您知道您为我节省了多少时间。++
约翰

不是else: shutil.copyfile(src, dest, ignore)吗?
杰克·詹姆斯

这是我不得不重复复制和删除时发现的唯一功能。
np8

1
@JackJames不,不应该这样,因为copyfile()它仅针对单个文件被调用。使用时,ignore您会将文件夹传递到src(否则,如果您忽略要传递的文件,则src根本不必调用该函数)。传递文件夹时,copyfile()永远不会调用被忽略的文件。
罗伊·丹顿

7

在Python 3.8中,dirs_exist_ok关键字参数已添加shutil.copytree()

dirs_exist_ok指示是否要引发异常,以防万一dst或任何丢失的父目录已经存在。

因此,即使目标目录已存在,以下内容也可以在最新版本的Python中运行:

shutil.copytree(src, dest, dirs_exist_ok=True)  # 3.8+ only!

一个主要的好处是它比distutils.dir_util.copy_tree()需要忽略文件上的其他参数等更具灵活性。还有一个PEP草案(PEP 632相关讨论),它建议distutils不要使用它,然后在以后的Python 3版本中将其删除。 。


0

我的简单答案。

def get_files_tree(src="src_path"):
    req_files = []
    for r, d, files in os.walk(src):
        for file in files:
            src_file = os.path.join(r, file)
            src_file = src_file.replace('\\', '/')
            if src_file.endswith('.db'):
                continue
            req_files.append(src_file)

    return req_files
def copy_tree_force(src_path="",dest_path=""):
    """
    make sure that all the paths has correct slash characters.
    """
    for cf in get_files_tree(src=src_path):
        df= cf.replace(src_path, dest_path)
        if not os.path.exists(os.path.dirname(df)):
            os.makedirs(os.path.dirname(df))
        shutil.copy2(cf, df)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.