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

发布于 2024-08-13 21:34:28 字数 957 浏览 7 评论 0原文

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

我希望它的工作方式与我输入的方式相同:

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

Do I need to use shutil.copy() to copy every file in baz into foo? (在我已经使用 shutil.copytree() 将“bar”的内容复制到“foo”中之后?)还是有更简单/更好的方法?

Run the following code from a directory that contains a directory named bar (containing one or more files) and a directory named baz (also containing one or more files). Make sure there is not a directory named foo.

import shutil
shutil.copytree('bar', 'foo')
shutil.copytree('baz', 'foo')

It will fail with:

$ 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'

I want this to work the same way as if I had typed:

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

Do I need to use shutil.copy() to copy each file in baz into foo? (After I've already copied the contents of 'bar' into 'foo' with shutil.copytree()?) Or is there an easier/better way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

誰認得朕 2024-08-20 21:34:28

这是标准库的一部分的解决方案:

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

请参阅此类似问题。

使用 python 将目录内容复制到目录中

Here's a solution that's part of the standard library:

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

See this similar question.

Copy directory contents into a directory with python

手长情犹 2024-08-20 21:34:28

标准 shutil.copytree 的这种限制看起来任意且令人讨厌。解决方法:

import os, shutil
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树;
  • 对于 src 根级别的错误,它不会引发 shutil.Error
  • 如果在复制子树期间出现错误,它将引发该子树的 shutil.Error,而不是尝试复制其他子树并引发单个组合的 shutil.Error

This limitation of the standard shutil.copytree seems arbitrary and annoying. Workaround:

import os, shutil
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)

Note that it's not entirely consistent with the standard copytree:

  • it doesn't honor symlinks and ignore parameters for the root directory of the src tree;
  • it doesn't raise shutil.Error for errors at the root level of src;
  • in case of errors during copying of a subtree, it will raise shutil.Error for that subtree instead of trying to copy other subtrees and raising single combined shutil.Error.
请爱~陌生人 2024-08-20 21:34:28

Python 3.8 引入了 dirs_exist_ok 参数到 < a href="https://docs.python.org/3/library/shutil.html#shutil.copytree" rel="noreferrer">shutil.copytree

递归地将以src为根的整个目录树复制到名为dst的目录并返回目标目录。 dirs_exist_ok 指示是否在 dst 或任何缺失的父目录已存在的情况下引发异常。

因此,对于 Python 3.8+,这应该可以工作:

import shutil

shutil.copytree('bar', 'foo')  # Will fail if `foo` exists
shutil.copytree('baz', 'foo', dirs_exist_ok=True)  # Fine

Python 3.8 introduced the dirs_exist_ok argument to shutil.copytree:

Recursively copy an entire directory tree rooted at src to a directory named dst and return the destination directory. dirs_exist_ok dictates whether to raise an exception in case dst or any missing parent directory already exists.

Therefore, with Python 3.8+ this should work:

import shutil

shutil.copytree('bar', 'foo')  # Will fail if `foo` exists
shutil.copytree('baz', 'foo', dirs_exist_ok=True)  # Fine
懒的傷心 2024-08-20 21:34:28

对 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)

在我上面的实现中,

  • 如果尚不存在,则创建输出目录
  • 通过递归调用我自己的方法来执行复制目录。
  • 当我们实际复制文件时,我检查文件是否被修改,然后才
    我们应该复制。

我正在使用上面的函数和 scons 构建。它对我帮助很大,因为每次编译时我可能不需要复制整组文件..而只需要复制修改的文件。

In slight improvement on atzz's answer to the function where the above function always tries to copy the files from source to destination.

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)

In my above implementation

  • Creating the output directory if not already exists
  • Doing the copy directory by recursively calling my own method.
  • When we come to actually copying the file I check if the file is modified then only
    we should copy.

I am using above function along with scons build. It helped me a lot as every time when I compile I may not need to copy entire set of files.. but only the files which are modified.

小情绪 2024-08-20 21:34:28

受 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 相同的行为,具有 符号链接忽略 参数
  • 如果不存在,则创建目录目标结构
  • 如果 dst 已经存在,则不会失败

A merge one inspired by atzz and 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)
  • Same behavior as shutil.copytree, with symlinks and ignore parameters
  • Create directory destination structure if non existant
  • Will not fail if dst already exists
眼角的笑意。 2024-08-20 21:34:28

文档明确指出目标目录不应该存在

dst命名的目标目录必须不存在;它将被创建以及缺少的父目录。

我认为你最好的选择是os.walk第二个和所有后续目录,copy2 目录和文件,并对目录执行额外的 copystat 操作。毕竟,这正是 copytree 所做的,如文档中所述。或者您可以复制copystat每个目录/文件和os.listdir而不是os.walk

docs explicitly state that destination directory should not exist:

The destination directory, named by dst, must not already exist; it will be created as well as missing parent directories.

I think your best bet is to os.walk the second and all consequent directories, copy2 directory and files and do additional copystat for directories. After all that's precisely what copytree does as explained in the docs. Or you could copy and copystat each directory/file and os.listdir instead of os.walk.

苏别ゝ 2024-08-20 21:34:28

这受到 atzz 提供的原始最佳答案的启发,我只是添加了替换文件/文件夹逻辑。因此它实际上并不合并,而是删除现有文件/文件夹并复制新文件/文件夹:

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 以使其成为移动函数。

This is inspired from the original best answer provided by atzz, I just added replace file / folder logic. So it doesn't actually merge, but deletes the existing file/ folder and copies the new one:

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)

Uncomment the rmtree to make it a move function.

我一直都在从未离去 2024-08-20 21:34:28

这是我对这个问题的看法。我修改了copytree的源代码以保留原始功能,但现在当目录已经存在时不会发生错误。我还更改了它,这样它就不会覆盖现有文件,而是保留两个副本,其中一个副本的名称经过修改,因为这对我的应用程序很重要。

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)

Here is my pass at the problem. I modified the source code for copytree to keep the original functionality, but now no error occurs when the directory already exists. I also changed it so it doesn't overwrite existing files but rather keeps both copies, one with a modified name, since this was important for my application.

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)
み青杉依旧 2024-08-20 21:34:28

这是一个需要 pathlib.Path 作为输入的版本。

# Recusively copies the content of the directory src to the directory dst.
# If dst doesn't exist, it is created, together with all missing parent directories.
# If a file from src already exists in dst, the file in dst is overwritten.
# Files already existing in dst which don't exist in src are preserved.
# Symlinks inside src are copied as symlinks, they are not resolved before copying.
#
def copy_dir(src, dst):
    dst.mkdir(parents=True, exist_ok=True)
    for item in os.listdir(src):
        s = src / item
        d = dst / item
        if s.is_dir():
            copy_dir(s, d)
        else:
            shutil.copy2(str(s), str(d))

请注意,此函数需要 Python 3.6,这是 os.listdir() 支持类似路径对象作为输入的第一个 Python 版本。如果您需要支持早期版本的Python,可以将listdir(src)替换为listdir(str(src))

Here is a version that expects a pathlib.Path as input.

# Recusively copies the content of the directory src to the directory dst.
# If dst doesn't exist, it is created, together with all missing parent directories.
# If a file from src already exists in dst, the file in dst is overwritten.
# Files already existing in dst which don't exist in src are preserved.
# Symlinks inside src are copied as symlinks, they are not resolved before copying.
#
def copy_dir(src, dst):
    dst.mkdir(parents=True, exist_ok=True)
    for item in os.listdir(src):
        s = src / item
        d = dst / item
        if s.is_dir():
            copy_dir(s, d)
        else:
            shutil.copy2(str(s), str(d))

Note that this function requires Python 3.6, which is the first version of Python where os.listdir() supports path-like objects as input. If you need to support earlier versions of Python, you can replace listdir(src) by listdir(str(src)).

人海汹涌 2024-08-20 21:34:28

这是我对同一任务的版本::

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)

Here is my version of the same task::

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)
转身以后 2024-08-20 21:34:28

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

updateonly 如果为 True,则为布尔值,将仅复制修改日期比 dst 中现有文件更新的文件,除非在 forceupdate 中列出,无论如何都会复制。

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

该函数返回复制的文件列表(或者如果dryrun为True则将被复制)。

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

Here is a version inspired by this thread that more closely mimics distutils.file_util.copy_file.

updateonly is a bool if True, will only copy files with modified dates newer than existing files in dst unless listed in forceupdate which will copy regardless.

ignore and forceupdate expect lists of filenames or folder/filenames relative to src and accept Unix-style wildcards similar to glob or fnmatch.

The function returns a list of files copied (or would be copied if dryrun if True).

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
决绝 2024-08-20 21:34:28

之前的解决方案存在一些问题,src 可能会在没有任何通知或异常的情况下覆盖 dst

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

最好首先使用 predict_error 来预测所有错误,除非您希望在执行 copytree 时看到另一个异常引发的异常,直到修复所有错误。

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)

The previous solution has some issue that src may overwrite dst without any notification or exception.

I add a predict_error method to predict errors before copy.copytree mainly base on Cyrille Pontvieux's version.

Using predict_error to predict all errors at first is best, unless you like to see exception raised one by another when execute copytree until fix all error.

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)
日裸衫吸 2024-08-20 21:34:28

试试这个:

import os,shutil

def copydir(src, dst):
  h = os.getcwd()
  src = r"{}".format(src)
  if not os.path.isdir(dst):
     print("\n[!] No Such directory: ["+dst+"] !!!")
     exit(1)

  if not os.path.isdir(src):
     print("\n[!] No Such directory: ["+src+"] !!!")
     exit(1)
  if "\\" in src:
     c = "\\"
     tsrc = src.split("\\")[-1:][0]
  else:
    c = "/"
    tsrc = src.split("/")[-1:][0]

  os.chdir(dst)
  if os.path.isdir(tsrc):
    print("\n[!] The Directory Is already exists !!!")
    exit(1)
  try:
    os.mkdir(tsrc)
  except WindowsError:
    print("\n[!] Error: In[ {} ]\nPlease Check Your Dirctory Path !!!".format(src))
    exit(1)
  os.chdir(h)
  files = []
  for i in os.listdir(src):
    files.append(src+c+i)
  if len(files) > 0:
    for i in files:
        if not os.path.isdir(i):
            shutil.copy2(i, dst+c+tsrc)

  print("\n[*] Done ! :)")

copydir("c:\folder1", "c:\folder2")

Try This:

import os,shutil

def copydir(src, dst):
  h = os.getcwd()
  src = r"{}".format(src)
  if not os.path.isdir(dst):
     print("\n[!] No Such directory: ["+dst+"] !!!")
     exit(1)

  if not os.path.isdir(src):
     print("\n[!] No Such directory: ["+src+"] !!!")
     exit(1)
  if "\\" in src:
     c = "\\"
     tsrc = src.split("\\")[-1:][0]
  else:
    c = "/"
    tsrc = src.split("/")[-1:][0]

  os.chdir(dst)
  if os.path.isdir(tsrc):
    print("\n[!] The Directory Is already exists !!!")
    exit(1)
  try:
    os.mkdir(tsrc)
  except WindowsError:
    print("\n[!] Error: In[ {} ]\nPlease Check Your Dirctory Path !!!".format(src))
    exit(1)
  os.chdir(h)
  files = []
  for i in os.listdir(src):
    files.append(src+c+i)
  if len(files) > 0:
    for i in files:
        if not os.path.isdir(i):
            shutil.copy2(i, dst+c+tsrc)

  print("\n[*] Done ! :)")

copydir("c:\folder1", "c:\folder2")
魄砕の薆 2024-08-20 21:34:28

我无法编辑上面的“Boris Dalstein”答案,因此这里是此代码的改进版本:

编辑所做的改进:

  • 输入参数可以是 str 路径或pathlib.Path 对象。键入提示会有所帮助。
  • 如果源是目录,它将创建该目录并
  • 为局部变量定义类型,因此 IDE 不会发出警告
# Recusively copies the content of the directory src to the directory dst.
# If dst doesn't exist, it is created, together with all missing parent directories.
# If a file from src already exists in dst, the file in dst is overwritten.
# Files already existing in dst which don't exist in src are preserved.
# Symlinks inside src are copied as symlinks, they are not resolved before copying.
#

def copy_dir(source: Union[str, pathlib.Path], destination: Union[str, pathlib.Path]):
    destination_path: pathlib.Path

    if isinstance(source, str):
        source_path = pathlib.Path(source)
    elif isinstance(source, pathlib.Path):
        source_path = source

    if isinstance(destination, str):
        destination_path = pathlib.Path(destination)
    elif isinstance(destination, pathlib.Path):
        destination_path = destination

    destination_path.mkdir(parents=True, exist_ok=True)
    if source_path.is_dir():
        destination_path = destination_path.joinpath(source_path.name)
        destination_path.mkdir(parents=True, exist_ok=True)

    for item in os.listdir(source_path):
        s: pathlib.Path = source_path / item
        d: pathlib.Path = destination_path / item
        if s.is_dir():
            copy_dir(s, d)
        else:
            shutil.copy2(str(s), str(d))

I couldn't edit the "Boris Dalstein" answer above so here is the improved version of this code:

EDIT on the improvements made:

  • The input args could be str path or pathlib.Path object. Type hint will help.
  • If the source is a directory, it will create that directory as well
  • types are defined for local variables so no warning by the IDE
# Recusively copies the content of the directory src to the directory dst.
# If dst doesn't exist, it is created, together with all missing parent directories.
# If a file from src already exists in dst, the file in dst is overwritten.
# Files already existing in dst which don't exist in src are preserved.
# Symlinks inside src are copied as symlinks, they are not resolved before copying.
#

def copy_dir(source: Union[str, pathlib.Path], destination: Union[str, pathlib.Path]):
    destination_path: pathlib.Path

    if isinstance(source, str):
        source_path = pathlib.Path(source)
    elif isinstance(source, pathlib.Path):
        source_path = source

    if isinstance(destination, str):
        destination_path = pathlib.Path(destination)
    elif isinstance(destination, pathlib.Path):
        destination_path = destination

    destination_path.mkdir(parents=True, exist_ok=True)
    if source_path.is_dir():
        destination_path = destination_path.joinpath(source_path.name)
        destination_path.mkdir(parents=True, exist_ok=True)

    for item in os.listdir(source_path):
        s: pathlib.Path = source_path / item
        d: pathlib.Path = destination_path / item
        if s.is_dir():
            copy_dir(s, d)
        else:
            shutil.copy2(str(s), str(d))
街角迷惘 2024-08-20 21:34:28

您可以尝试这个简单的修复:

import shutil
shutil.copytree('bar', 'foo')
shutil.copytree('baz', 'foo', dirs_exist_ok=True)

文档

如果dirs_exist_ok为true,则如果遇到现有目录,复制操作将继续,并且dst树中的文件将被src树中的相应文件覆盖。

You can try this simple fix:

import shutil
shutil.copytree('bar', 'foo')
shutil.copytree('baz', 'foo', dirs_exist_ok=True)

From the Documentation:

If dirs_exist_ok is true, the copying operation will continue if it encounters existing directories, and files within the dst tree will be overwritten by corresponding files from the src tree.

︶葆Ⅱㄣ 2024-08-20 21:34:28

我假设最快和最简单的方法是让 python 调用系统命令...

例如..

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

Tar 和 gzip 目录...将目录解压并解压到所需位置。

啊?

i would assume fastest and simplest way would be have python call the system commands...

example..

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

Tar and gzip up the directory.... unzip and untar the directory in the desired place.

yah?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文