كيف يمكنني نسخ دليل كامل للملفات في دليل موجود باستخدام Python؟

StackOverflow https://stackoverflow.com/questions/1868714

  •  18-09-2019
  •  | 
  •  

سؤال

قم بتشغيل التعليمات البرمجية التالية من دليل يحتوي على دليل يدعى 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/

هل أحتاج إلى استخدام shutil.copy() لنسخ كل ملف في baz إلى fooب (بعد نسخ محتويات "شريط" في "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 القياسي:

  • لا يكرم symlinks و ignore معلمات الدليل الجذر لل src شجرة؛
  • لا يرفع shutil.Error للحصول على أخطاء على مستوى الجذر src;
  • في حالة وجود أخطاء أثناء نسخ الشجرة الفرعية، سترفع shutil.Error بالنسبة لتلك الشجرة الفرعية بدلا من محاولة نسخ الفئات الفرعية الأخرى وترفع واحدة مجتمعة shutil.Error.

نصائح أخرى

إليك حل جزء من المكتبة القياسية.

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

انظر هذا السؤال المماثل.

نسخ محتويات الدليل في دليل مع بيثون

في تحسين طفيف على إجابة 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 Build. لقد ساعدني كثيرا في كل مرة عندما أرغم أنني قد لا أحتاج إلى نسخ مجموعة كاملة من الملفات .. ولكن فقط الملفات التي تم تعديلها.

دمج واحدة مستوحاة من التميز والزيج فورا:

#!/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., ، مع symlinks. و يتجاهل المعلمات
  • إنشاء هيكل الوجهة الدليل إذا غير موجود
  • لن تفشل إذا توليد موجود أصلا

تذكر مستندات هذا الدليل الوجهة صراحة ليس يخرج:

دليل الوجهة، اسمه dst, ، يجب أن غير موجود بالفعل؛ سيتم إنشاؤها وكذلك أدلة الوالدين المفقودة.

أعتقد أن أفضل رهان الخاص بك هو os.walk والثاني وكل الدلائل الناتجة، copy2 دليل الملفات والملفات copystat للحصول على الدلائل. بعد كل هذا بدقة ما copytree يفعل كما هو موضح في المستندات. أو يمكنك ذلك copy و copystat كل دليل / ملف و os.listdir بدلا من os.walk.

يمكنك تعديل shutil والحصول على التأثير (على نسختي من shutil هذا على الخط 315)

يتغيرون

os.makedirs(dst)

ل

os.makedirs(dst,exist_ok=True)

هذا مستوحى من أفضل الإجابة الأصلية المقدمة من 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)

uncomment the rmtree لجعلها وظيفة خطوة.

أود أن أفترض أن أسرع وأبسط طريقة ستكون لها ثعبون أوامر النظام ...

مثال..

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

القطران و Gzip حتى الدليل .... UNZIP و UNDARD الدليل في المكان المطلوب.

ياه؟

إليك نسختي من نفس المهمة ::

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 هل BOOL إذا كان هذا صحيحا، فستسخن الملفات فقط مع التواريخ المعدلة أحدث من الملفات الموجودة في dst ما لم تدرج في forceupdate والتي سوف نسخ بغض النظر.

ignore و forceupdate توقع قوائم بأسماء الملفات أو المجلد / الملفات على صلة قربى ب src وقبول أحرف البدل على غرار يونيكس مماثلة ل glob أو fnmatch.

تقوم الدالة بإرجاع قائمة الملفات المنسوخة (أو سيتم نسخها إذا dryrun إذا كان هذا صحيحا).

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

الحل السابق لديه بعض القضية src قد التنشيط dst دون أي إشعار أو استثناء.

أضف أ predict_error طريقة للتنبؤ بالأخطاء قبل النسخ.copytree أساسا أساسا على نسخة سيريل بونتيفيوس.

استخدام 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)

هنا مروري في المشكلة. قمت بتعديل التعليمات البرمجية المصدرية ل 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)

جرب هذا:

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")
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top