คัดลอกไฟล์หรือไดเรกทอรีซ้ำใน Python


117

Python ดูเหมือนจะมีฟังก์ชันสำหรับคัดลอกไฟล์ (เช่นshutil.copy) และฟังก์ชันสำหรับคัดลอกไดเร็กทอรี (เช่นshutil.copytree) แต่ฉันไม่พบฟังก์ชันใด ๆ ที่จัดการทั้งสองอย่าง แน่นอนว่าการตรวจสอบว่าคุณต้องการคัดลอกไฟล์หรือไดเรกทอรีนั้นเป็นเรื่องเล็กน้อย แต่ดูเหมือนว่าจะเป็นการละเลยที่แปลกประหลาด

ไม่มีฟังก์ชั่นมาตรฐานที่ทำงานเหมือนกับcp -rคำสั่งunix นั่นคือรองรับทั้งไดเร็กทอรีและไฟล์และคัดลอกซ้ำหรือไม่? วิธีใดเป็นวิธีที่ดีที่สุดในการแก้ไขปัญหานี้ใน Python


3
ใช่นี่เป็นระเบียบ หนึ่งในสถานที่ที่พยายามสะท้อนการเรียกระบบพื้นฐาน Python ทำให้อินเทอร์เฟซที่มองเห็นได้แย่ลง แม้ว่าจะไม่ใช่เรื่องยากที่จะสลับไปมาระหว่าง copy-file และ copy-tree แต่ก็ไม่จำเป็นต้องมี อาจส่งคำขอการปรับปรุงในตัวติดตามข้อบกพร่องของ Python เพื่ออนุญาตให้copytreeคัดลอกไฟล์เดียว?
bobince

คำตอบ:


143

ผมแนะนำให้คุณสายแรกและถ้ายกเว้นจะโยนแล้วลองอีกครั้งกับshutil.copytreeshutil.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
ฉันคิดว่ามันจะดีกว่ามากถ้าจะตรวจสอบว่า src เป็นไดเร็กทอรีโดยใช้ os.path.isdir (src) แทนที่จะจับข้อยกเว้นเช่นนี้ หรือมีเหตุผลพิเศษบางประการที่ควรใช้ข้อยกเว้นที่นี่แทน?
pafcu

31
1) เนื่องจากในโลกของ Python EAFP (ง่ายกว่าที่จะขอการให้อภัยมากกว่าการอนุญาต) เป็นที่ต้องการของ LBYL (ดูก่อนกระโดด) ฉันสามารถให้ลิงก์เกี่ยวกับเรื่องนี้แก่คุณได้หากฟังดูใหม่สำหรับคุณ 2) ฟังก์ชั่นไลบรารีตรวจสอบทางอ้อมแล้วทำไมต้องทำซ้ำการตรวจสอบ? 3) ไม่มีสิ่งใดหยุดshutil.copytreeฟังก์ชันจากการปรับปรุงและจัดการทั้งสองกรณีในอนาคต 4) ข้อยกเว้นไม่ได้เป็นข้อยกเว้นใน Python เช่นการวนซ้ำจะหยุดลงโดยทิ้งข้อยกเว้น StopIteration
tzot

2
ในกรณีนี้การจัดการข้อยกเว้นจะใช้เวลา 6 บรรทัดในขณะที่การตรวจสอบประเภทใช้เวลา 4 บรรทัด ไม่มาก แต่จะเพิ่มขึ้นในตอนท้าย อย่างที่คุณบอกว่าสักวันหนึ่ง copytree อาจรองรับไฟล์ได้เป็นอย่างดีเช่นกัน แต่เป็นไปไม่ได้ที่จะบอกว่าการนำไปใช้นั้นเป็นอย่างไร บางทีอาจมีข้อยกเว้นภายใต้สถานการณ์บางอย่างที่การคัดลอกทำงาน? ในกรณีนั้นรหัสของฉันจะหยุดทำงานกะทันหันเพียงเพราะฟังก์ชันที่เพิ่มเข้ามา แต่คุณอาจจะพูดถูกข้อยกเว้นค่อนข้างเป็นที่นิยมใน Python ซึ่งเป็นสิ่งที่ฉันคิดว่าน่ารำคาญมาก แต่อาจเป็นเพราะฉันไม่เคยชินกับมัน
pafcu

5
ข้อยกเว้นที่จริงมีข้อได้เปรียบที่ชัดเจนประการหนึ่งในกรณีนี้: เป็นไปได้ทั้งหมด (แม้ว่าจะไม่น่าเป็นไปได้สูงก็ตาม) ที่ประเภทจะเปลี่ยนไประหว่างการตรวจสอบและการเรียกใช้ฟังก์ชันที่ถูกต้อง
pafcu

2
ในความเห็นส่วนตัวของฉันการเพิ่มฟังก์ชันหลักในข้อยกเว้นเป็นการปฏิบัติที่ไม่ดีไม่สำคัญว่าคุณจะใช้ภาษาใด ทำให้ฟังก์ชันการทำงานเป็นที่ที่นักพัฒนาจำนวนมากไม่ได้ค้นหา ยิ่งไปกว่านั้นหากคุณไม่เขียนความคิดเห็นผู้พัฒนา python ที่มีประสบการณ์น้อยจะไม่เข้าใจว่าจุดประสงค์ของการลองใหม่นี้คืออะไร และหากคุณต้องการเพิ่มความคิดเห็นสำหรับเรื่องเล็กน้อยเช่นที่นี่มีบางอย่างในรูปแบบโค้ดของคุณไม่ถูกต้อง ในที่สุดการเขียน if / else จะทำให้อ่านโค้ดได้ง่ายขึ้นมาก
this.myself

7

หากต้องการเพิ่มคำตอบของ Tzotและgnsนี่เป็นอีกทางเลือกหนึ่งในการคัดลอกไฟล์และโฟลเดอร์แบบวนซ้ำ (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)

หากเป็นครั้งแรกของคุณและคุณไม่รู้วิธีคัดลอกไฟล์และโฟลเดอร์ซ้ำฉันหวังว่านี่จะช่วยได้


3

shutil.copyและshutil.copy2กำลังคัดลอกไฟล์

shutil.copytreeคัดลอกโฟลเดอร์ที่มีไฟล์ทั้งหมดและโฟลเดอร์ย่อยทั้งหมด shutil.copytreeกำลังใช้shutil.copy2เพื่อคัดลอกไฟล์

ดังนั้นอนาล็อกเพื่อcp -rที่คุณพูดเป็นshutil.copytreeเพราะcp -rเป้าหมายและสำเนาโฟลเดอร์และไฟล์ / shutil.copytreeโฟลเดอร์ย่อยเช่น โดยไม่ต้อง-r cpคัดลอกไฟล์เช่นshutil.copyและshutil.copy2ทำ


1
ฉันไม่คิดว่าคุณเข้าใจคำถาม ลองshutil.copytree('C:\myfile.txt', 'C:\otherfile'). มันใช้ไม่ได้ นั่นคือสิ่งที่ OP กำลังถามถึง ... 7 ปีที่แล้ว
Jean-François Corbett

แน่นอนว่ามันใช้ไม่ได้ เช่นเดียวกับ cp ไม่ทำงานกับโฟลเดอร์ คุณต้องมีตัวเลือกพิเศษ การคัดลอกและ copytree เป็นวิธีที่ดีที่สุดในการจัดการการคัดลอก หาก copytree สามารถกำหนดเป้าหมายและไฟล์ได้ก็จะเกิดข้อผิดพลาดได้ง่าย คุณต้องรู้ว่าคุณกำลังกำหนดเป้าหมายอะไรทั้งกับ Linux และ Python ที่ยาก นอกจากนี้ยังมีคนอื่นแสดงความคิดเห็นที่นี่ แต่เมื่อเห็นคำถามและคำตอบก็อดที่จะตอบไม่ได้ วิธีที่ดีคือการรู้ว่าคุณต้องการทำอะไรไม่ใช่สำเนาสากลที่ไม่มีการควบคุมใด ๆ
gms

2

Unix cpไม่ 'รองรับทั้งไดเร็กทอรีและไฟล์':

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

ในการสร้าง cp copy ไดเร็กทอรีคุณต้องบอก cp ด้วยตนเองว่าเป็นไดเร็กทอรีโดยใช้แฟล็ก '-r'

มีการตัดการเชื่อมต่อที่นี่ - cp -rเมื่อส่งผ่านชื่อไฟล์เนื่องจากแหล่งที่มาจะคัดลอกเพียงไฟล์เดียวอย่างมีความสุข copytree จะไม่


2
docs.python.org/library/shutil.htmlมีโค้ดสำหรับ copytree () ซึ่งแสดงให้เห็นถึงการจัดการไฟล์ทั่วไป symlinks และไดเร็กทอรี
James Polley

1
คำตอบนี้ไม่ตรงกับคำถาม มันควรจะเป็นความคิดเห็นไม่ใช่คำตอบ
Jean-François Corbett


-2

python shutil.copytree วิธีการเป็นระเบียบ ฉันได้ทำสิ่งที่ทำงานได้อย่างถูกต้อง:

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
รหัสนี้เหมาะสำหรับการตรวจสอบไฟล์แต่ละไฟล์ (ตรวจสอบปัญหาการเขียนทับ) แต่จะใช้ไม่ได้กับไฟล์ไบนารีเช่น 'zip' ทำไมไม่ใช้การคัดลอกไฟล์ python แบบธรรมดาแทนการอ่าน / เขียนทีละบรรทัด
notilas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.