在Python中递归复制文件或目录


116

Python似乎具有复制文件的功能(例如shutil.copy)和复制目录的功能(例如shutil.copytree),但是我还没有找到能够同时处理两者的函数。当然,检查要复制文件还是目录很简单,但这似乎很奇怪。

真的没有标准功能可以像unix cp -r命令那样工作,即是否同时支持目录和文件以及递归复制?在Python中解决此问题的最优雅方法是什么?


3
是的,这是一团糟。Python试图反映底层系统调用的地方之一,使可见界面变得更糟。尽管在复制文件和复制树之间切换并不困难,但它不是必需的。也许在Python错误跟踪器上提出了增强请求,以允许copytree复制单个文件?
bobince 2010年

Answers:


142

建议您先致电shutil.copytree,如果引发异常,请重试shutil.copy

import shutil, errno

def copyanything(src, dst):
    try:
        shutil.copytree(src, dst)
    except OSError as exc: # python >2.5
        if exc.errno == errno.ENOTDIR:
            shutil.copy(src, dst)
        else: raise

18
我认为使用os.path.isdir(src)来简单地检查src是否为目录,而不是捕获这样的异常会更清洁。还是有一些特殊的原因要在这里使用异常?
pafcu 2010年

31
1)因为在python世界中,EAFP(请求宽恕比允许更容易)比LBYL(飞跃前请先看)更受欢迎。如果您觉得很新,我可以为您提供相关链接。2)库函数已经间接检查过,为什么要重复检查呢?3)shutil.copytree将来没有什么功能可以阻止改进和管理这两种情况。4)异常在Python中并不是那么例外;例如,迭代通过抛出StopIteration异常而停止。
tzot 2010年

2
好吧,在这种情况下,处理异常需要6行,而检查类型需要4行。数量不多,但最终累加起来。而且,正如您所说,copytree有时也可能很好地支持文件。但是,无法确定该实现将是什么样子。也许在某些情况下在复制有效的情况下会引发异常?在那种情况下,由于添加的功能,我的代码会突然停止工作。但是您可能是对的,异常在Python中非常常见,我觉得这很烦人,但这可能是因为我似乎从未习惯过
pafcu 2010年

5
实际上,在这种情况下,异常确实具有一个明显的客观优势:完全有可能(尽管极不可能)在检查和对正确函数的调用之间改变类型。
pafcu 2010年

2
我个人认为,在除外中添加核心功能是不好的做法,与您使用哪种语言无关。它将功能放在了很多开发人员都不会搜索的地方。此外,如果您不写评论,那么经验不足的python开发人员将不会真正理解此重试的目的。如果您需要为像这样的琐事添加注释,则您的代码样式有误。最后,编写一个if / else将使代码易于阅读。
this.myself15年

7

要在增加Tzot的GNS答案,这里的递归复制文件和文件夹的另一种方式。(Python 3.X)

import os, shutil

root_src_dir = r'C:\MyMusic'    #Path/Location of the source directory
root_dst_dir = 'D:MusicBackUp'  #Path to the destination folder

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):
            os.remove(dst_file)
        shutil.copy(src_file, dst_dir)

如果这是您的第一次,并且您不知道如何递归地复制文件和文件夹,希望对您有所帮助。


3

shutil.copyshutil.copy2正在复制文件。

shutil.copytree复制包含所有文件和所有子文件夹的文件夹。shutil.copytree正在shutil.copy2用来复制文件。

因此,cp -r您所说的类似物是shutil.copytree因为cp -r定位并复制文件夹及其文件/子文件夹,例如shutil.copytree。如果没有-r cp文件,如副本shutil.copyshutil.copy2做的。


1
我认为您不理解这个问题。尝试shutil.copytree('C:\myfile.txt', 'C:\otherfile')。没用 这就是OP在7年前问的问题。
让·弗朗索瓦·科贝特

当然不行了。就像cp不适用于文件夹。您需要一个特殊的选择。copy和copytree是处理复制的最佳方法。如果copytree可以定位并归档,则很容易出错。您必须知道Linux和Python的目标。辛苦了 再加上其他人在这里发表了评论,但看到问题和答复都无法拒绝回答。优雅的方法是知道您要做什么,而不是没有任何控制的通用副本。
gms

2

Unix cp不“支持目录和文件”:

betelgeuse:tmp james$ cp source/ dest/
cp: source/ is a directory (not copied).

要使cp复制目录,必须使用'-r'标志手动告诉cp它是目录。

但是这里有些脱节- cp -r当传递文件名时,源将很高兴地仅复制单个文件。复制树不会。


2
docs.python.org/library/shutil.html包含用于copytree()的代码,该代码演示了如何处理普通文件,符号链接和目录。
詹姆斯·波利

1
这个答案没有解决这个问题。它应该是评论,而不是答案。
让·弗朗索瓦·科贝特


-2

python shutil.copytree方法很混乱。我做了一个可以正常工作的方法:

def copydirectorykut(src, dst):
    os.chdir(dst)
    list=os.listdir(src)
    nom= src+'.txt'
    fitx= open(nom, 'w')

    for item in list:
        fitx.write("%s\n" % item)
    fitx.close()

    f = open(nom,'r')
    for line in f.readlines():
        if "." in line:
            shutil.copy(src+'/'+line[:-1],dst+'/'+line[:-1])
        else:
            if not os.path.exists(dst+'/'+line[:-1]):
                os.makedirs(dst+'/'+line[:-1])
                copydirectorykut(src+'/'+line[:-1],dst+'/'+line[:-1])
            copydirectorykut(src+'/'+line[:-1],dst+'/'+line[:-1])
    f.close()
    os.remove(nom)
    os.chdir('..')

1
此代码非常适合工作单个文件检查(检查覆盖问题),但不适用于二进制文件(例如“ zip”)。为什么不使用简单的python文件复制而不是逐行读取/写入?
notilas
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.