Python kullanarak tüm dosya dizinini mevcut bir dizine nasıl kopyalarım?


210

Aşağıdaki kodu adlı bir dizinden bar(bir veya daha fazla dosya içeren) ve bir dizinden baz(ayrıca bir veya daha fazla dosya içeren) bir dizinden çalıştırın. Adında bir dizin olmadığından emin olun foo.

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

Şununla başarısız olur:

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

Bu yazmış gibi aynı şekilde çalışmak istiyorum:

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

shutil.copy()Her dosyayı baziçine kopyalamak için kullanmam gerekir foomi? (Zaten 'bar' içeriğini 'foo' shutil.copytree()ya kopyaladıktan sonra ?) Yoksa daha kolay / daha iyi bir yol var mı?


1
FYI: İşte orijinal copytree işlevi, sadece kopyalayın ve yama yapın :)
schlamar

3
Varolan bir dizine yazmaya izin vermek için davranışın değiştirilmesiyle ilgilishutil.copytree() bir Python sorunu var , ancak üzerinde anlaşılması gereken bazı davranış ayrıntıları var.
Nick Chammas

2
Sadece yukarıda belirtilen geliştirme talebinin Python 3.8 için uygulandığını not edin: docs.python.org/3.8/whatsnew/3.8.html#shutil
ncoghlan

Yanıtlar:


174

Standardın bu sınırlaması shutil.copytreekeyfi ve sinir bozucu görünmektedir. Çözüm:

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)

Standartla tamamen tutarlı olmadığını unutmayın copytree:

  • ağacın kök dizini için onur symlinksve ignoreparametreler sağlamaz src;
  • shutil.Errorkök düzeyindeki hatalar için yükselmez src;
  • bir alt ağacın kopyalanması sırasında hata olması durumunda, shutil.Errordiğer alt ağaçların kopyalanması ve birleştirilmiş tek bir alt kümenin yükseltilmesi yerine bu alt ağaç için yükselecektir shutil.Error.

50
Teşekkürler! Bunun tamamen keyfi göründüğünü kabul edin! başında shutil.copytreebir an os.makedirs(dst). Kodun hiçbir bölümünde önceden var olan bir dizinle ilgili bir sorun yoktur. Bunun değiştirilmesi gerekiyor. En azından exist_ok=Falseçağrıya bir parametre sağlayın
cfi

6
Bu iyi bir cevap - ancak Mital Vora'nın aşağıdaki cevabı da incelemeye değer. Aynı sorun başka türlü ortaya çıkacağından shutil.copytree () öğesini çağırmak yerine copytree'yi özyinelemeli olarak çağırdılar. Yanıtları birleştirmeyi veya Mital Vora'nın güncellemesini düşünebilirsiniz.
PJeffes

4
Hedefte boş olmayan bir dizin içeren bir yol verilirse bu başarısız olur. Belki biri bunu kuyruk özyineleme ile çözebilir, ancak işte kodunuzda çalışan bir değişiklikdef 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): if os.path.isdir(d): self.recursiveCopyTree(s, d, symlinks, ignore) else: shutil.copytree(s, d, symlinks, ignore) else: shutil.copy2(s, d)
Sojurn

8
Meh, sinir bozucu. 4 yıl sonra ve shutil.copytree hala bu saçma kısıtlamaya sahip. :-(
antred

5
@antred ... ama distutils.dir_util.copy_tree()hangi zamanda , stdlib ikamet böyle bir sınırlama yoktur ve aslında beklendiği gibi davranır. Bu göz önüne alındığında, kendi ( ... genellikle bozuk ) uygulamanızı açmaya çalışmak için zorlayıcı bir neden yoktur . Brendan Abel 'in cevabı kesinlikle artık kabul çözüm olmalıdır.
Cecil Curry

257

Standart kütüphanenin bir parçası olan bir çözüm:

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

Benzer soruya bakın.

Dizin içeriğini python ile bir dizine kopyalama


5
Bu iyi bir kitap çünkü standart kütüphaneyi kullanıyor. Semboller, mod ve zaman da korunabilir.
itsafire

1
Küçük bir dezavantaj fark ettim. distutils.errors.DistutilsInternalError: mkpath: 'name' must be a string, yani kabul etmez PosixPath. Gerek str(PosixPath). İyileştirme için istek listesi. Bu meselenin dışında bu cevabı tercih ediyorum.
Sun Bear

@SunBear, Evet, bence yolları dize alan diğer kütüphanelerin çoğunda durum böyle olacak. Nesneye yönelik yol nesnelerinin önceki uygulamalarının çoğu gibi, sanırım Pathnesneyi miras almamayı seçmenin dezavantajı str..
Brendan Abel

Btw, bu fonksiyonun belgelenmiş bir eksikliğiyle karşılaştım. Belgeli burada . Bu fonksiyonun kullanıcıları bunun farkında olma tavsiyesinde bulunmuşlardır.
Ayı

1
"Teknik olarak herkese açık" olsa da, dağıtımcıların geliştiricilerinin, dağıtımcıların uygulama ayrıntısı olarak kabul edilen ve genel kullanım için önerilmez , bunu açıkça ifade ettiğini (@ SunBear's, thx! distutils.dir_util.copy_tree()Asıl çözüm, shutil.copytree()daha fazla davranacak distutils.dir_util.copy_tree()şekilde, eksiklikleri olmadan iyileştirilecek / genişletilecek olmalıdır . Bu arada, diğer yanıtlarda sağlananlara benzer özel yardımcı işlevleri kullanmaya devam edeceğim.
Boris Dalstein

61

Atzz'in yukarıdaki işlevin her zaman dosyaları kaynaktan hedefe kopyalamaya çalıştığı işleve cevabında küçük bir iyileşme.

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)

Yukarıdaki uygulamamda

  • Henüz yoksa çıktı dizini oluşturma
  • Kendi yöntemimi yinelemeli olarak çağırarak kopyalama dizinini yapmak.
  • Aslında dosyayı kopyalamaya geldiğimizde dosyanın değiştirilip değiştirilmediğini kontrol ederim.

Scons build ile birlikte yukarıdaki işlevi kullanıyorum. Derlediğim her zaman olduğu gibi bana çok yardımcı oldu. Dosyaların tümünü kopyalamaya gerek olmayabilir .. ama sadece değiştirilen dosyaları.


4
Hoş, ancak sembolik bağlar var ve argüman olarak görmezden gelmek, ama yok sayılır.
Matthew Alpert

St_mtime ayrıntı düzeyinin FAT dosya sistemlerinde docs.python.org/2/library/os.html dosyasında 2 saniye kadar kaba olabileceğini belirtmek gerekir . Bu kodu, güncellemelerin hızlı bir şekilde gerçekleştiği bir bağlamda kullanarak, geçersiz kılmaların gerçekleşmediğini görebilirsiniz.
dgh

İkinci-son satırda bir hata var, şöyle olmalıdır: if not os.path.exists(d) or os.stat(s).st_mtime - os.stat(d).st_mtime > 1:
mpderbec

34

Atzz ve Mital Vora'dan esinlenilen birleşme:

#!/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)
  • Aynı davranışı shutil.copytree ile, sembolik ve görmezden parametreleri
  • Varsa dizin hedef yapısı oluşturun
  • Dst zaten varsa başarısız olmaz

Dizin yerleştirme derin olduğunda bu orijinal çözümden çok daha hızlıdır. Teşekkürler
Kashif

Başka bir yerde kodda 'yoksay' adında bir işlev tanımladınız mı?
KenV99

Copytree işlevini çağırmadan önce istediğiniz herhangi bir adla herhangi bir işlevi tanımlayabilirsiniz. Bu işlev (aynı zamanda bir lambda ifadesi de olabilir) iki argüman alır: bir dizin adı ve içindeki dosyalar, yinelenebilir dosyaları yoksaymalı olarak döndürmelidir.
Cyrille Pontvieux

[x for x in lst if x not in excl]bu, glob desen eşleşmesini kullanan copytree ile aynı şey değildir. en.wikipedia.org/wiki/Glob_(programming)
Konstantin Schubert

2
Bu harika. Yukarıdaki cevapta görmezden gelinme doğru kullanılmıyordu.
Keith Holliday

21

Python 3.8 tanıtıldı dirs_exist_okargüman için shutil.copytree:

Src köklü tüm dizin ağacını özyineli olarak dst adlı bir dizine kopyalayın ve hedef dizini döndürün . dirs_exist_ok , dst veya eksik herhangi bir üst dizinin zaten mevcut olması durumunda bir istisnanın yükseltilip yükseltilmeyeceğini belirler .

Bu nedenle, Python 3.8+ ile bu çalışmalıdır:

import shutil

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

dirs_exist_ok=Falsecopytree'de varsayılan olarak, ilk kopyalama denemesi başarısız olmaz mı?
Jay

1
@Jay, yalnızca dizin zaten varsa. dirs_exist_okFarkı göstermek için ilk çağrıdan ayrıldım (ve dizin OP örneğinde henüz mevcut olmadığından), ancak elbette isterseniz kullanabilirsiniz.
Chris

Teşekkürler, ilk kopyaya yakın bir yorum eklerseniz, daha net olacağını düşünüyorum :)
Jay


1

Bu atzz tarafından sağlanan orijinal en iyi cevaptan esinlenerek, sadece dosya / klasör mantığını değiştirmeyi ekledim. Yani aslında birleştirilmez, ancak mevcut dosyayı / klasörü siler ve yenisini kopyalar:

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)

Bir hareket fonksiyonu yapmak için rmtree'yi açın.


0

İşte aynı görevin benim sürümü:

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)

0

İşte bu konudan esinlenen ve daha yakından taklit eden bir versiyon distutils.file_util.copy_file.

updateonlyTrue ise bool ise, değiştirilmiş tarihler mevcut dosyalardan daha yeni olan dosyaları, ne olursa olsun kopyalanacağı dstlistelenmedikçe forceupdatekopyalar.

ignoreve forceupdatedosya adları veya klasörün listelerini bekliyoruz / filenames göre src Unix tarzı benzer joker kabul edip globya fnmatch.

İşlev kopyalanan dosyaların bir listesini döndürür (veya dryrunTrue ise kopyalanır ).

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

0

Önceki çözümde, herhangi bir bildirim veya istisna olmadan srcüzerine yazılabilecek bazı sorunlar var dst.

predict_errorKopyalamadan önce hataları tahmin etmek için bir yöntem ekliyorum . copytreeesas olarak Cyrille Pontvieux'un versiyonuna dayanıyor.

Kullanılması predict_errorEğer yürütmek istisnası başka teker kaldırdı görmek ister sürece ilk başta tüm hataları tahmin etmek, en iyisi copytreedüzeltme tüm hata dek.

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)

0

İşte benim sorunumdan geçişim. Özgün işlevselliği korumak için copytree için kaynak kodunu değiştirdim, ancak dizin zaten var olduğunda hiçbir hata oluşmuyor. Ayrıca var olan dosyaların üzerine yazmaz, bunun yerine her iki kopyayı da değiştirilmiş bir ada sahip tutar, çünkü bu benim uygulama için önemliydi.

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)

0

Bunu dene:

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

0

Burada a pathlib.Patholarak girdi bekleyen bir sürüm var .

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

Bu işlevin, Python'un ilk sürümü olan Python 3.6'yı gerektirdiğini unutmayın. os.listdir() giriş olarak yol benzeri nesneleri desteklediği . Eğer Python önceki sürümlerini desteklemek için gerekiyorsa, yerine listdir(src)göre listdir(str(src)).


-2

en hızlı ve en basit yolu sistem komutlarını çağırmak python olurdu varsayalım ...

misal..

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

Katlayın ve dizini açın ... dizini istediğiniz yerde açın ve açın.

yah?


Windows'da çalışıyorsanız ... 7zip'i indirin ve bunun için komut satırını kullanın. ... yine sadece öneriler.
Kirby

31
Sistem komutları her zaman son çare olmalıdır. Kodunuzun taşınabilir olması için mümkün olduğunca standart kitaplığı kullanmak her zaman daha iyidir.
jathanizm
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.