Python'da dosya veya dizinleri yinelemeli olarak kopyalayın


117

Python, dosyaları kopyalamak için işlevlere (örneğin shutil.copy) ve dizinleri kopyalamak için işlevlere (örneğin ) sahip gibi görünüyor, shutil.copytreeancak her ikisini de işleyen herhangi bir işlev bulamadım. Elbette, bir dosyayı mı yoksa bir dizini mi kopyalamak istediğinizi kontrol etmek önemsizdir, ancak garip bir ihmal gibi görünüyor.

Gerçekten de unix cp -rkomutu gibi çalışan , yani hem dizinleri hem de dosyaları destekleyen ve özyinelemeli olarak kopyalayan standart bir işlev yok mu? Python'da bu sorunu aşmanın en zarif yolu nedir?


3
Evet, bu bir karmaşa. Python'un temeldeki sistem çağrılarını yansıtmaya çalışarak görünür arayüzü daha da kötüleştirdiği yerlerden biri. Dosya kopyala ve ağaç kopyala arasında geçiş yapmak zor olmasa da gerekli olmamalıydı. Belki copytreetek bir dosyanın kopyalanmasına izin vermek için Python hata izleyicisine bir geliştirme isteğinde bulunabilirsiniz ?
bobince

Yanıtlar:


143

Önce aramanızı öneririm shutil.copytree, eğer bir istisna olursa, ile tekrar deneyin shutil.copy.

import shutil, errno

def copyanything(src, dst):
    try:
        shutil.copytree(src, dst)
    except OSError as exc: # python >2.5
        if exc.errno == errno.ENOTDIR:
            shutil.copy(src, dst)
        else: raise

18
Bunun gibi bir istisnayı yakalamak yerine src'nin os.path.isdir (src) kullanan bir dizin olup olmadığını kontrol etmenin çok daha temiz olacağını düşünüyorum. Yoksa burada bir istisna kullanmanın özel bir nedeni var mı?
pafcu

31
1) Çünkü Python dünyasında EAFP (af dilemek izin istemekten daha kolaydır) LBYL'ye (atlamadan önce bakın) tercih edilir. Size yeni geliyorsa, size bununla ilgili bağlantılar sağlayabilirim. 2) Kitaplık işlevi zaten dolaylı olarak bunu kontrol ediyor, öyleyse neden kontrolü kopyalıyorsunuz? 3) hiçbir şey, shutil.copytreeişlevi gelecekte her iki durumu da iyileştirmekten ve yönetmekten alıkoyamaz. 4) Python'da istisnalar o kadar istisnai değildir; örneğin, bir StopIteration istisnası atılarak yineleme durur.
tzot

2
Pekala, bu durumda istisnayı ele almak 6 satır alırken, türü kontrol etmek 4 satır alır. Fazla değil ama sonunda toparlanıyor. Ayrıca, sizin de söylediğiniz gibi, copytree bir gün dosyaları çok iyi destekleyebilir. Ancak bu uygulamanın nasıl olacağını söylemek imkansız. Belki de kopyalamanın çalıştığı bazı durumlarda bir istisna atar? Bu durumda kodum, sadece eklenen işlevsellik nedeniyle aniden çalışmayı durdururdu. Ama muhtemelen haklısınız, Python'da istisnalar oldukça yaygın, çok sinir bozucu bulduğum bir şey, ama muhtemelen buna hiç
alışamadığım içindir

5
Aslında istisnaların bu durumda net bir nesnel avantajı vardır: tipin kontrol ve doğru işleve çağrı arasında değişmesi tamamen mümkündür (pek olası olmasa da).
pafcu

2
kişisel görüşüme göre, bir dışında temel işlevler eklemek kötü bir uygulamadır, hangi dili kullanıyor olursanız olun. birçok geliştiricinin aramayacağı bir yere işlevsellik katar. ayrıca, bir yorum yazmazsanız, daha az deneyimli bir python geliştiricisi, bu yeniden denemenin amacının ne olduğunu gerçekten anlayamaz. ve buradaki gibi önemsiz bir şey için bir yorum eklemeniz gerekirse, kod stilinizle ilgili bir şey yanlış. son olarak, if / else yazmak kodun okunması çok daha kolay olacaktır.
Kendim

7

Tzot'un ve gns yanıtlarını eklemek için , dosya ve klasörleri yinelemeli olarak kopyalamanın alternatif bir yolu. (Python 3.X)

import os, shutil

root_src_dir = r'C:\MyMusic'    #Path/Location of the source directory
root_dst_dir = 'D:MusicBackUp'  #Path to the destination folder

for src_dir, dirs, files in os.walk(root_src_dir):
    dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
    if not os.path.exists(dst_dir):
        os.makedirs(dst_dir)
    for file_ in files:
        src_file = os.path.join(src_dir, file_)
        dst_file = os.path.join(dst_dir, file_)
        if os.path.exists(dst_file):
            os.remove(dst_file)
        shutil.copy(src_file, dst_dir)

İlk seferinizse ve dosyaları ve klasörleri yinelemeli olarak nasıl kopyalayacağınız hakkında hiçbir fikriniz yoksa, umarım bu yardımcı olur.


3

shutil.copy ve shutil.copy2 dosyaları kopyalıyor.

shutil.copytree tüm dosyaları ve tüm alt klasörleri içeren bir klasörü kopyalar. shutil.copytreeshutil.copy2dosyaları kopyalamak için kullanıyor .

Demek istediğin analog cp -r, shutil.copytreeçünkü cp -rbir klasörü ve onun dosyalarını / alt klasörlerini hedefler ve kopyalar shutil.copytree. -r cpKopyalar olmadan dosyaları beğenin shutil.copyve yapın shutil.copy2.


1
Soruyu anladığını sanmıyorum. Deneyin shutil.copytree('C:\myfile.txt', 'C:\otherfile'). Çalışmıyor. OP'nin sorduğu şey buydu ... 7 yıl önce.
Jean-François Corbett

Tabii ki çalışmıyor. Cp'nin klasörlerle çalışmaması gibi. Özel bir seçeneğe ihtiyacınız var. kopyalama ve kopyalama, kopyalama işleminin en iyi yoludur. Copytree hedefleyebilir ve dosyaları kaydedebilseydi, hata yapmak kolay olurdu. Hem Linux hem de Python ile neyi hedeflediğinizi bilmelisiniz. Bu zor. Ayrıca başka biri de burada yorum yaptı, ancak soruyu ve yanıtları görünce cevaplanmaya karşı koyamadı. Zarif yol, ne yapmak istediğinizi bilmektir, herhangi bir kontrolün olmadığı evrensel bir kopya değil.
gms

2

Unix cp, hem dizinleri hem de dosyaları desteklemez:

betelgeuse:tmp james$ cp source/ dest/
cp: source/ is a directory (not copied).

Cp'nin bir dizini kopyalamasını sağlamak için, '-r' bayrağını kullanarak cp'ye bunun bir dizin olduğunu manuel olarak söylemeniz gerekir.

Yine de burada bazı kopukluklar var - cp -rbir dosya adı geçildiğinde, kaynak mutlu bir şekilde sadece tek dosyayı kopyalayacaktır; copytree olmayacak.


2
docs.python.org/library/shutil.html , normal dosyaların, sembolik bağların ve dizinlerin işlenmesini gösteren copytree () kodunu içerir.
James Polley

1
Bu cevap soruyu ele almıyor. Bir cevap değil, bir yorum olmalı.
Jean-François Corbett


-2

Python shutil.copytree yöntemi tam bir karmaşa. Doğru çalışan bir tane yaptım:

def copydirectorykut(src, dst):
    os.chdir(dst)
    list=os.listdir(src)
    nom= src+'.txt'
    fitx= open(nom, 'w')

    for item in list:
        fitx.write("%s\n" % item)
    fitx.close()

    f = open(nom,'r')
    for line in f.readlines():
        if "." in line:
            shutil.copy(src+'/'+line[:-1],dst+'/'+line[:-1])
        else:
            if not os.path.exists(dst+'/'+line[:-1]):
                os.makedirs(dst+'/'+line[:-1])
                copydirectorykut(src+'/'+line[:-1],dst+'/'+line[:-1])
            copydirectorykut(src+'/'+line[:-1],dst+'/'+line[:-1])
    f.close()
    os.remove(nom)
    os.chdir('..')

1
Bu kod, iş için tek tek dosya denetimi için iyidir (üzerine yazma sorununu kontrol edin), ancak 'zip' gibi ikili dosyalar için çalışmayacaktır. Neden satır satır okuma / yazma yerine basit python dosyası kopyası kullanmıyorsunuz?
notilas
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.