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



كتابة برنامج بلغة بايثون (8)

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

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

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

نسخ محتويات الدليل إلى دليل مع 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'

أريد أن يعمل هذا بالطريقة نفسها كما لو كنت قد كتبت:

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

هل أحتاج إلى استخدام shutil.copy() لنسخ كل ملف في baz إلى foo ؟ (بعد أن قمت بالفعل بنسخ محتويات 'bar' إلى 'foo' مع shutil.copytree() ؟) أو هل هناك طريقة أسهل / أفضل؟


Answer #1

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


Answer #2

هذا التحديد من مستوى 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)

لاحظ أنه لا يتطابق تمامًا مع النسخة القياسية للنسخة:

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

Answer #3

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


Answer #4

هنا نسخة مستوحاة من هذا الموضوع الذي يقلد عن كثب distutils.file_util.copy_file .

updateonly هو bool إذا كان True ، سيقوم فقط بنسخ الملفات ذات التواريخ المعدلة الأحدث من الملفات الموجودة في dst ما لم يتم سردها في forceupdate والتي سيتم نسخها بغض النظر.

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

تقوم الدالة بإرجاع قائمة من الملفات المنسوخة (أو سيتم نسخها في حالة 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

Answer #5

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

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)

Answer #6

يحتوي الحل السابق على بعض src التي قد تقوم 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)

Answer #7

يمكنك تعديل shutil والحصول على التأثير

يتغيرون

os.makedirs(dst)

إلى

os.makedirs(dst,exist_ok=True)




copytree