Python-移动和覆盖文件和文件夹


71

我有一个目录“ Dst Directory”,其中包含文件和文件夹,而我有“ src Directory”,其中也包含文件和文件夹。我想要做的是将“ src目录”的内容移动到“ Dst目录”,并覆盖具有相同名称的所有文件。因此,例如需要将“ Src Directory \ file.txt”移至“ Dst Directory \”并覆盖现有的file.txt。对于某些文件夹,移动文件夹并将内容与“ dst目录”中的同一文件夹合并,也是如此

我当前正在使用shutil.move将src的内容移动到dst,但是如果文件已经存在并且不会合并文件夹,则不会这样做。它将只将该文件夹放在现有文件夹中。

更新:为了使事情更加清楚,我正在做的是将存档解压缩到Dst目录,然后将Src目录的内容移到那里并重新压缩,从而有效地更新了zip存档中的文件。添加新文件或文件的新版本等将重复此操作,这就是为什么它需要覆盖和合并的原因

已解决:我通过使用distutils.dir_util.copy_tree(src,dst)解决了我的问题,这会将文件夹和文件从src目录复制到dst目录,并在需要时覆盖/合并。希望对某些人有所帮助!

希望有道理,谢谢!


请注意,distutils.dir_util.copy_tree它不能复制特殊文件,例如命名管道(throws distutils.errors.DistutilsFileError)。
patryk.beza

Answers:


52

copy()改用,它会覆盖目标文件。如果您随后希望第一棵树消失,则在rmtree()对其进行遍历之后,将其分开。

http://docs.python.org/library/shutil.html#shutil.copy

http://docs.python.org/library/shutil.html#shutil.rmtree

更新:

做一个os.walk()在源代码树。对于每个目录,请检查目标目录中是否存在目录,是否os.makedirs()缺少目录。对于每个文件,只要shutil.copy()合适就可以简单地创建和覆盖该文件。


copy()不能复制文件夹,可以吗?
Artharos 2011年

否,但是move()在每个文件上也不会创建目标目录,因此我假设您的代码已经os.makedirs()在目标文件夹上执行了一个不存在的目标文件夹。啊! 我想我现在明白了-您一次move()整棵树上做一个?哥奇亚 会更新我的答案。
布兰登·罗兹

感谢您的更新,这样做的问题是要复制的文件总是在变化(添加了新文件等),因此,如果您了解这一点,那么每次添加新文件进行移动时,我都必须更新代码。无论如何,我使用distutils.dir_util.copy_tree(src,dst)对其进行管理,它复制文件夹和文件,并在
必要时

os.walk()会在每次运行时为您提供源文件的最新列表,因此,如果我理解您的问题,则列表每次都可以更改。但是distutils,尽管有了这个解决方案,还是很幸运–当有人依靠distutils而不是stdlib时,这总是冒险!我怀疑他们会保持该功能正常运行。
布兰登·罗兹

哦,我明白了,我不太了解os.walk方法,我假设您必须定义要使用shutil.copy复制的文件。我现在了解了,已经很长时间没有使用os.walk了。我会正确回答这个问题,因为我看到它可以正常工作,但由于简单性,我现在还是坚持使用distutils
Artharos 2011年

61

这将遍历源目录,创建目标目录中尚不存在的任何目录,并将文件从源移动到目标目录:

import os
import shutil

root_src_dir = 'Src Directory\\'
root_dst_dir = 'Dst Directory\\'

for src_dir, dirs, files in os.walk(root_src_dir):
    dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    for file_ in files:
        src_file = os.path.join(src_dir, file_)
        dst_file = os.path.join(dst_dir, file_)
        if os.path.exists(dst_file):
            # in case of the src and dst are the same file
            if os.path.samefile(src_file, dst_file):
                continue
            os.remove(dst_file)
        shutil.move(src_file, dst_dir)

所有先前存在的文件都将首先(通过os.remove)删除,然后再用相应的源文件替换。目标中已存在但源中不存在的所有文件或目录将保持不变。


3
很好,谢谢!我认为这就是Brandon Craig Rhodes所说的,但感谢您提供摘录!不幸的是你不能有两个正确的答案^^
Artharos 2011年

2
复制它们就像将“ shutil.move”替换为“ shutil.copy”一样容易。
Karoh 2012年

1
如果root_dst_dir =='dir1 \\ dir2 \\',则os.mkdir(dst_dir)将引发错误,提示“没有这样的文件或目录:'dir1 \\ dir2 \\'”。使用os.makedirs(dst_dir)可以避免此问题。
布莱恩(Brian)

1
我已经编辑了答案,以包含@lyen和我自己指出的修复程序。这个解决方案对我来说效果很好。谢谢!
2015年

1
如何删除root_src_dir中的空目录?使用rmdir时出现Errorno 16
Ram

9

由于以上都不适合我,因此我编写了自己的递归函数。调用函数copyTree(dir1,dir2)合并目录。在多平台Linux和Windows上运行。

def forceMergeFlatDir(srcDir, dstDir):
    if not os.path.exists(dstDir):
        os.makedirs(dstDir)
    for item in os.listdir(srcDir):
        srcFile = os.path.join(srcDir, item)
        dstFile = os.path.join(dstDir, item)
        forceCopyFile(srcFile, dstFile)

def forceCopyFile (sfile, dfile):
    if os.path.isfile(sfile):
        shutil.copy2(sfile, dfile)

def isAFlatDir(sDir):
    for item in os.listdir(sDir):
        sItem = os.path.join(sDir, item)
        if os.path.isdir(sItem):
            return False
    return True


def copyTree(src, dst):
    for item in os.listdir(src):
        s = os.path.join(src, item)
        d = os.path.join(dst, item)
        if os.path.isfile(s):
            if not os.path.exists(dst):
                os.makedirs(dst)
            forceCopyFile(s,d)
        if os.path.isdir(s):
            isRecursive = not isAFlatDir(s)
            if isRecursive:
                copyTree(s, d)
            else:
                forceMergeFlatDir(s, d)

使用其他答案时,哪种情况对您不起作用?
2015年

1
值得注意的是,如果src在dst中具有与目录相同名称的文件,则此解决方案会将文件放入共享其名称的目录中,而Ray Vega的解决方案将抛出OSError: [Errno 21] Is a directory
2015年

这工作完美。没有OSError 10/12/16/21 ...我在尝试sys.move()时遇到了很多错误。谢谢 。
拉姆

4

如果还需要用只读标志覆盖文件,请使用以下命令:

def copyDirTree(root_src_dir,root_dst_dir):
"""
Copy directory tree. Overwrites also read only files.
:param root_src_dir: source directory
:param root_dst_dir:  destination directory
"""
for src_dir, dirs, files in os.walk(root_src_dir):
    dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    for file_ in files:
        src_file = os.path.join(src_dir, file_)
        dst_file = os.path.join(dst_dir, file_)
        if os.path.exists(dst_file):
            try:
                os.remove(dst_file)
            except PermissionError as exc:
                os.chmod(dst_file, stat.S_IWUSR)
                os.remove(dst_file)

        shutil.copy(src_file, dst_dir)

1

看看:os.remove删除现有文件。


问题在于,我要添加到文件夹中的文件将更改(将添加新文件,并更新旧文件),因此我无法确定要删除的内容的列表,谢谢
Artharos 2011年

0

我有一个类似的问题。我想移动文件和文件夹结构并覆盖现有文件,但不删除目标文件夹结构中的任何内容。

我通过使用os.walk(),递归调用我的函数并使用来解决了shutil.move()在要覆盖的文件和不存在的文件夹上。

它的工作方式类似于shutil.move(),但是好处是仅覆盖现有文件,而不删除它们。

import os
import shutil

def moverecursively(source_folder, destination_folder):
    basename = os.path.basename(source_folder)
    dest_dir = os.path.join(destination_folder, basename)
    if not os.path.exists(dest_dir):
        shutil.move(source_folder, destination_folder)
    else:
        dst_path = os.path.join(destination_folder, basename)
        for root, dirs, files in os.walk(source_folder):
            for item in files:
                src_path = os.path.join(root, item)
                if os.path.exists(dst_file):
                    os.remove(dst_file)
                shutil.move(src_path, dst_path)
            for item in dirs:
                src_path = os.path.join(root, item)
                moverecursively(src_path, dst_path)

1
您的代码将无效,dst_file根本没有定义。
辛巴
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.