Salin file atau direktori secara rekursif dengan Python

116

Python tampaknya memiliki fungsi untuk menyalin file (misalnya shutil.copy) dan fungsi untuk menyalin direktori (misalnya shutil.copytree) tetapi saya belum menemukan fungsi yang menangani keduanya. Tentu, memeriksa apakah Anda ingin menyalin file atau direktori adalah hal yang mudah, tetapi sepertinya ada kelalaian yang aneh.

Apakah benar-benar tidak ada fungsi standar yang bekerja seperti cp -rperintah unix , yaitu mendukung direktori dan file serta salinan secara rekursif? Apa cara paling elegan untuk mengatasi masalah ini dengan Python?

pafcu
sumber
3
Ya, ini berantakan. Salah satu tempat, dengan mencoba mencerminkan panggilan sistem yang mendasarinya, Python membuat antarmuka yang terlihat menjadi lebih buruk. Meskipun tidak sulit untuk beralih antara copy-file dan copy-tree, ini seharusnya tidak diperlukan. Mungkin mengajukan permintaan peningkatan pada pelacak bug Python untuk memungkinkan copytreemenyalin satu file?
bobince

Jawaban:

142

Saya sarankan Anda menelepon dulu shutil.copytree, dan jika ada pengecualian, coba lagi dengan 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
tzot.dll
sumber
18
Saya pikir akan jauh lebih bersih untuk hanya memeriksa apakah src adalah direktori yang menggunakan os.path.isdir (src) daripada menangkap pengecualian seperti ini. Atau adakah alasan khusus seseorang harus menggunakan pengecualian di sini?
pafcu
31
1) Karena di dunia Python, EAFP (lebih mudah meminta maaf daripada izin) lebih disukai daripada LBYL (lihat sebelum Anda melompat). Saya dapat memberi Anda tautan tentang itu, jika itu terdengar baru bagi Anda. 2) Fungsi perpustakaan sudah secara tidak langsung memeriksa itu, jadi mengapa mereplikasi cek tersebut? 3) tidak ada yang menghentikan shutil.copytreefungsi untuk meningkatkan dan mengelola kedua kasus di masa mendatang. 4) Pengecualian tidak terlalu luar biasa di Python; misalnya sebuah iterasi berhenti dengan menampilkan pengecualian StopIteration.
tzot
2
Nah, dalam hal ini penanganan exception membutuhkan 6 baris, sedangkan pemeriksaan type membutuhkan 4 baris. Tidak banyak, tapi akhirnya bertambah. Juga, seperti yang Anda katakan, copytree suatu hari nanti mungkin juga mendukung file dengan sangat baik. Tetapi tidak mungkin untuk mengatakan seperti apa implementasi itu nantinya. Mungkin itu melempar pengecualian dalam beberapa keadaan di mana salinan berfungsi? Dalam hal ini kode saya tiba-tiba berhenti berfungsi hanya karena fungsionalitas tambahan. Tetapi Anda mungkin benar, pengecualian cukup umum dalam Python, sesuatu yang menurut saya sangat mengganggu, tetapi mungkin karena saya tidak pernah terbiasa dengannya
pafcu
5
Sebenarnya pengecualian memang memiliki satu keuntungan objektif yang jelas dalam kasus ini: sangat mungkin (meskipun sangat tidak mungkin) bahwa tipe berubah antara check dan panggilan ke fungsi yang benar.
pafcu
2
menurut pendapat pribadi saya, menambahkan fungsi inti dalam pengecualian adalah praktik yang buruk, tidak masalah bahasa apa yang Anda gunakan. itu menempatkan fungsionalitas pada suatu tempat, di mana banyak pengembang tidak akan mencari. lebih lanjut, jika Anda tidak menulis komentar, pengembang python yang kurang berpengalaman tidak akan benar-benar mengerti apa tujuan dari percobaan ulang ini. dan jika Anda perlu menambahkan komentar untuk hal sepele seperti di sini, ada sesuatu pada gaya kode Anda yang salah. akhirnya, menulis if / else akan menghasilkan kode yang lebih mudah dibaca.
sendiri
7

Untuk menambah Tzot ini dan GNS jawaban, inilah alternatif cara menyalin file dan folder secara rekursif. (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)

Jika ini pertama kalinya Anda dan Anda tidak tahu cara menyalin file dan folder secara rekursif, saya harap ini membantu.

mondieki
sumber
3

shutil.copydan shutil.copy2sedang menyalin file.

shutil.copytreemenyalin folder dengan semua file dan semua subfolder. shutil.copytreedigunakan shutil.copy2untuk menyalin file.

Jadi analogi yang cp -rAnda katakan adalahshutil.copytree karena cp -rmenargetkan dan menyalin folder dan file / subfolder seperti itu shutil.copytree. Tanpa -r cpsalinan file like shutil.copyand shutil.copy2do.

gms
sumber
1
Saya tidak berpikir Anda mengerti pertanyaan itu. Coba shutil.copytree('C:\myfile.txt', 'C:\otherfile'). Itu tidak berhasil. Itulah yang ditanyakan OP ... 7 tahun yang lalu.
Jean-François Corbett
Tentu saja tidak berhasil. Seperti cp tidak berfungsi dengan folder. Anda membutuhkan opsi khusus. copy dan copytree adalah cara terbaik untuk menangani penyalinan. Jika copytree dapat menargetkan dan mengarsipkan, maka akan mudah membuat kesalahan. Anda harus tahu apa yang Anda targetkan dengan Linux dan Python. Itu sulit. Ditambah orang lain mengomentarinya di sini, tetapi melihat pertanyaan dan jawabannya tidak bisa menahan untuk menjawab. Cara elegan adalah mengetahui apa yang ingin Anda lakukan dan bukan salinan universal tanpa kendali apa pun.
RUPS
2

Unix cp tidak 'mendukung direktori dan file':

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

Untuk membuat cp menyalin direktori, Anda harus memberi tahu cp secara manual bahwa itu adalah direktori, dengan menggunakan tanda '-r'.

Ada beberapa pemutusan di sini meskipun - cp -rketika melewati nama file sebagai sumber akan dengan senang hati menyalin hanya satu file; copytree tidak akan.

James Polley
sumber
2
docs.python.org/library/shutil.html menyertakan kode untuk copytree () yang mendemonstrasikan penanganan file, symlink, dan direktori biasa.
James Polley
1
Jawaban ini tidak menjawab pertanyaan tersebut. Ini harus berupa komentar, bukan jawaban.
Jean-François Corbett
0

Saya pikir copy_tree adalah yang Anda cari

algorythms
sumber
-2

Metode python shutil.copytree itu berantakan. Saya telah melakukan satu yang berfungsi dengan benar:

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('..')
kutenzo
sumber
1
Kode ini bagus untuk pekerjaan pemeriksaan file individual (periksa masalah penimpaan), tetapi tidak akan berfungsi untuk file biner seperti 'zip'. Mengapa tidak menggunakan salinan file python sederhana daripada membaca / menulis baris demi baris?
notilas