如何使用Python将整个文件目录复制到现有目录中?

从包含名为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' 

我希望这样工作,就像我input的一样:

 $ mkdir foo $ cp bar/* foo/ $ cp baz/* foo/ 

我是否需要使用shutil.copy()baz每个文件复制到foo ? (在我用shutil.copytree()把'bar'的内容复制到'foo' shutil.copytree() )还是有更简单/更好的方法?

标准shutil.copytree这个限制看起来是任意的和烦人的。 解决方法:

 def copytree(src, dst, symlinks=False, ignore=None): for item in os.listdir(src): s = os.path.join(src, item) d = os.path.join(dst, item) if os.path.isdir(s): shutil.copytree(s, d, symlinks, ignore) else: shutil.copy2(s, d) 

请注意,它与标准的copytree不完全一致:

  • 它不尊重symlinksignore src树根目录的参数;
  • 它不会引发shutil.Errorsrc的根级错误;
  • 如果在复制子树期间发生错误,则会引发该子树的shutil.Error ,而不是尝试复制其他子树并引发单个组合的shutil.Error

这是一个解决scheme,是标准库的一部分。

 from distutils.dir_util import copy_tree copy_tree("/a/b/c", "/x/y/z") 

看到这个类似的问题。

用python将目录内容复制到一个目录

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) 

在我上面的实现

  • 如果不存在,则创build输出目录
  • 通过recursion调用我自己的方法来做复制目录。
  • 当我们实际上复制文件时,我检查文件是否被修改,只有我们应该复制。

我正在使用上面的函数与scons构build。 当我编译时,它帮助了我很多,我可能不需要复制整个文件集,但只有被修改的文件。

一个由atzz和Mital Vora启发的合并:

 #!/usr/bin/python import os import shutil import stat def copytree(src, dst, symlinks = False, ignore = None): 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: shutil.copy2(s, d) 
  • shutil.copytree具有相同的行为, 符号链接忽略参数
  • 如果不存在,创build目录目标结构
  • 如果dst已经存在,则不会失败

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

dst命名的目标目录不能存在; 它将被创build以及丢失的父目录。

我认为你最好的select是os.walk第二个和所有后续的目录, copy2目录和文件,并为目录做额外的copystat 。 毕竟这正是copytree在文档中解释的那样。 或者你可以copycopystat每个目录/文件和os.listdir而不是os.walk

这是从atzz提供的原始最好的答案启发,我只是添加replace文件/文件夹的逻辑。 所以它实际上并不合并,但删除了现有的文件/文件夹并复制新的文件/文件夹:

 import shutil import os def copytree(src, dst, symlinks=False, ignore=None): for item in os.listdir(src): s = os.path.join(src, item) d = os.path.join(dst, item) if os.path.exists(d): try: shutil.rmtree(d) except Exception as e: print e os.unlink(d) if os.path.isdir(s): shutil.copytree(s, d, symlinks, ignore) else: shutil.copy2(s, d) #shutil.rmtree(src) 

取消rmtree的注释使其成为移动function。

您可以修改shutil并获得效果

更改

 os.makedirs(dst) 

 os.makedirs(dst,exist_ok=True) 

我会假设最快和最简单的方法将有python调用系统命令…

例..

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

焦油和gzip的目录….解压缩和解压目录在所需的地方。

呀?

这是我的版本相同的任务::

 import os, glob, shutil def make_dir(path): if not os.path.isdir(path): os.mkdir(path) def copy_dir(source_item, destination_item): if os.path.isdir(source_item): make_dir(destination_item) sub_items = glob.glob(source_item + '/*') for sub_item in sub_items: copy_dir(sub_item, destination_item + '/' + sub_item.split('/')[-1]) else: shutil.copy(source_item, destination_item) 

这是一个受此线程启发的版本,更接近模仿distutils.file_util.copy_file

updateonly是一个布尔如果为True,只会复制修改后的date比dst现有文件更新的文件,除非在forceupdate中列出, forceupdate将会复制。

ignoreforceupdate期望的文件名或文件夹/文件名相对于 src列表,并接受类似于globfnmatch Unix风格的通配符。

该函数返回复制的文件列表(如果为真,则会被复制)。

 import os import shutil import fnmatch import stat import itertools def copyToDir(src, dst, updateonly=True, symlinks=True, ignore=None, forceupdate=None, dryrun=False): def copySymLink(srclink, destlink): if os.path.lexists(destlink): os.remove(destlink) os.symlink(os.readlink(srclink), destlink) try: st = os.lstat(srclink) mode = stat.S_IMODE(st.st_mode) os.lchmod(destlink, mode) except OSError: pass # lchmod not available fc = [] if not os.path.exists(dst) and not dryrun: os.makedirs(dst) shutil.copystat(src, dst) if ignore is not None: ignorepatterns = [os.path.join(src, *x.split('/')) for x in ignore] else: ignorepatterns = [] if forceupdate is not None: forceupdatepatterns = [os.path.join(src, *x.split('/')) for x in forceupdate] else: forceupdatepatterns = [] srclen = len(src) for root, dirs, files in os.walk(src): fullsrcfiles = [os.path.join(root, x) for x in files] t = root[srclen+1:] dstroot = os.path.join(dst, t) fulldstfiles = [os.path.join(dstroot, x) for x in files] excludefiles = list(itertools.chain.from_iterable([fnmatch.filter(fullsrcfiles, pattern) for pattern in ignorepatterns])) forceupdatefiles = list(itertools.chain.from_iterable([fnmatch.filter(fullsrcfiles, pattern) for pattern in forceupdatepatterns])) for directory in dirs: fullsrcdir = os.path.join(src, directory) fulldstdir = os.path.join(dstroot, directory) if os.path.islink(fullsrcdir): if symlinks and dryrun is False: copySymLink(fullsrcdir, fulldstdir) else: if not os.path.exists(directory) and dryrun is False: os.makedirs(os.path.join(dst, dir)) shutil.copystat(src, dst) for s,d in zip(fullsrcfiles, fulldstfiles): if s not in excludefiles: if updateonly: go = False if os.path.isfile(d): srcdate = os.stat(s).st_mtime dstdate = os.stat(d).st_mtime if srcdate > dstdate: go = True else: go = True if s in forceupdatefiles: go = True if go is True: fc.append(d) if not dryrun: if os.path.islink(s) and symlinks is True: copySymLink(s, d) else: shutil.copy2(s, d) else: fc.append(d) if not dryrun: if os.path.islink(s) and symlinks is True: copySymLink(s, d) else: shutil.copy2(s, d) return fc 

以前的解决scheme有一些问题, src可能会覆盖dst没有任何通知或例外。

我添加了一个predict_error方法来预测复制之前的错误。 copytree主要基于Cyrille Pontvieux的版本。

首先使用predict_error来预测所有的错误是最好的,除非你喜欢在执行copytree时一个接一个地看到exception,直到修复所有的错误。

 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) 

这是我的问题。 我修改了copytree的源代码以保持原来的function,但是现在当目录已经存在时不会发生错误。 我也改变它,所以它不覆盖现有的文件,而是保留两个副本,一个修改后的名称,因为这是我的应用程序的重要。

 import shutil import os def _copytree(src, dst, symlinks=False, ignore=None): """ This is an improved version of shutil.copytree which allows writing to existing folders and does not overwrite existing files but instead appends a ~1 to the file name and adds it to the destination path. """ names = os.listdir(src) if ignore is not None: ignored_names = ignore(src, names) else: ignored_names = set() if not os.path.exists(dst): os.makedirs(dst) shutil.copystat(src, dst) errors = [] for name in names: if name in ignored_names: continue srcname = os.path.join(src, name) dstname = os.path.join(dst, name) i = 1 while os.path.exists(dstname) and not os.path.isdir(dstname): parts = name.split('.') file_name = '' file_extension = parts[-1] # make a new file name inserting ~1 between name and extension for j in range(len(parts)-1): file_name += parts[j] if j < len(parts)-2: file_name += '.' suffix = file_name + '~' + str(i) + '.' + file_extension dstname = os.path.join(dst, suffix) i+=1 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) except (IOError, os.error) as why: errors.append((srcname, dstname, str(why))) # catch the Error from the recursive copytree so that we can # continue with other files except BaseException as err: errors.extend(err.args[0]) try: shutil.copystat(src, dst) except WindowsError: # can't copy file access times on Windows pass except OSError as why: errors.extend((src, dst, str(why))) if errors: raise BaseException(errors)