Sao chép tệp hoặc thư mục một cách đệ quy bằng Python


116

Python dường như có các hàm để sao chép tệp (ví dụ shutil.copy) và các hàm để sao chép thư mục (ví dụ shutil.copytree) nhưng tôi chưa tìm thấy bất kỳ hàm nào xử lý cả hai. Chắc chắn, việc kiểm tra xem bạn muốn sao chép một tệp hay một thư mục là một việc tầm thường, nhưng nó có vẻ là một thiếu sót kỳ lạ.

Có thực sự không có chức năng tiêu chuẩn nào hoạt động như cp -rlệnh unix , tức là hỗ trợ cả thư mục và tệp và sao chép đệ quy? Cách tốt nhất để giải quyết vấn đề này trong Python là gì?


3
Vâng, đây là một mớ hỗn độn. Một trong những nơi mà bằng cách cố gắng phản ánh các lệnh gọi hệ thống cơ bản, Python làm cho giao diện hiển thị trở nên tồi tệ hơn. Mặc dù không khó để chuyển đổi giữa copy-file và copy-tree, nhưng điều đó không cần thiết. Có thể gửi yêu cầu nâng cao trên trình theo dõi lỗi Python để cho phép copytreesao chép một tệp duy nhất?
bobince

Câu trả lời:


142

Tôi khuyên bạn nên gọi trước shutil.copytreevà nếu có ngoại lệ, hãy thử lại với 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
Tôi nghĩ sẽ gọn gàng hơn nhiều nếu chỉ cần kiểm tra xem src có phải là thư mục sử dụng os.path.isdir (src) hay không thay vì bắt một ngoại lệ như thế này. Hoặc có một số lý do đặc biệt mà người ta nên sử dụng một ngoại lệ ở đây để thay thế?
pafcu

31
1) Bởi vì trong thế giới Python, EAFP (dễ xin tha hơn xin phép) được ưu tiên hơn LBYL (hãy xem trước khi bạn nhảy). Tôi có thể cung cấp cho bạn các liên kết về điều đó, nếu nó có vẻ mới đối với bạn. 2) Chức năng thư viện đã gián tiếp kiểm tra điều đó, vậy tại sao lại kiểm tra lại? 3) không có gì ngăn cản shutil.copytreechức năng cải thiện và quản lý cả hai trường hợp trong tương lai. 4) Các ngoại lệ không phải là ngoại lệ trong Python; ví dụ: một lần lặp dừng bằng cách ném ra một ngoại lệ StopIteration.
tzot

2
Trong trường hợp này, xử lý ngoại lệ mất 6 dòng, trong khi kiểm tra loại mất 4 dòng. Không nhiều, nhưng cuối cùng nó sẽ tăng lên. Ngoài ra, như bạn nói, một ngày nào đó copytree cũng có thể hỗ trợ rất tốt các tệp. Nhưng không thể nói trước được việc triển khai đó sẽ như thế nào. Có thể nó ném một ngoại lệ trong một số trường hợp mà bản sao hoạt động? Trong trường hợp đó, mã của tôi sẽ đột ngột ngừng hoạt động chỉ vì chức năng được thêm vào. Nhưng bạn có lẽ đúng, trường hợp ngoại lệ được khá commong bằng Python, một cái gì đó mà tôi thấy rất khó chịu, nhưng nó có thể là bởi vì tôi không bao giờ dường như quen với nó
pafcu

5
Trên thực tế, các ngoại lệ có một lợi thế khách quan rõ ràng trong trường hợp này: hoàn toàn có thể xảy ra (mặc dù rất khó xảy ra) rằng kiểu thay đổi giữa séc và lệnh gọi hàm đúng.
pafcu

2
theo ý kiến ​​cá nhân của tôi, thêm chức năng cốt lõi trong một ngoại trừ là một phương pháp không tốt, không quan trọng bạn đang sử dụng ngôn ngữ nào. nó đưa chức năng vào một nơi mà nhiều nhà phát triển sẽ không tìm kiếm. hơn nữa, nếu bạn không viết nhận xét, một nhà phát triển python ít kinh nghiệm sẽ không thực sự hiểu mục đích của việc thử lại này là gì. và nếu bạn cần thêm một nhận xét cho một điều tầm thường như ở đây, thì kiểu mã của bạn đã sai. cuối cùng, viết if / else sẽ dẫn đến mã dễ đọc hơn nhiều.
this. Bản thân tôi

7

Để thêm vào câu trả lời của Tzotgns , đây là một cách thay thế để sao chép các tệp và thư mục một cách đệ quy. (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)

Nếu đây là lần đầu tiên của bạn và bạn không biết cách sao chép các tệp và thư mục một cách đệ quy, tôi hy vọng điều này sẽ hữu ích.


3

shutil.copyshutil.copy2đang sao chép các tập tin.

shutil.copytreesao chép một thư mục với tất cả các tệp và tất cả các thư mục con. shutil.copytreeđang sử dụng shutil.copy2để sao chép các tệp.

Vì vậy, tương tự như cp -rbạn đang nói là shutil.copytreecp -rnhắm mục tiêu và sao chép một thư mục và các tệp / thư mục con của nó như thế nào shutil.copytree. Mà không cần -r cpbản sao các tập tin như shutil.copyshutil.copy2làm.


1
Tôi không nghĩ rằng bạn hiểu câu hỏi. Cố gắng shutil.copytree('C:\myfile.txt', 'C:\otherfile'). Nó không hoạt động. Đó là những gì OP đã hỏi về ... 7 năm trước.
Jean-François Corbett,

Tất nhiên là nó không hoạt động. Giống như cp không hoạt động với các thư mục. Bạn cần một tùy chọn đặc biệt. copy và copytree là cách tốt nhất để xử lý việc sao chép. Nếu copytree có thể nhắm mục tiêu và các tệp thì sẽ rất dễ mắc lỗi. Bạn phải biết những gì bạn đang nhắm mục tiêu cả với Linux và Python. Khó đó. Thêm vào đó ai đó đã nhận xét nó ở đây, nhưng nhìn thấy câu hỏi và câu trả lời, không thể cưỡng lại để trả lời. Cách thanh lịch là biết bạn muốn làm gì và không phải là một bản sao phổ quát mà không có bất kỳ sự kiểm soát nào.
gms

2

Unix cpkhông 'hỗ trợ cả thư mục và tệp':

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

Để làm cho cp sao chép một thư mục, bạn phải nói với cp theo cách thủ công rằng đó là một thư mục, bằng cách sử dụng cờ '-r'.

Tuy nhiên, có một số ngắt kết nối ở đây - cp -rkhi được chuyển một tên tệp làm nguồn sẽ vui vẻ sao chép chỉ tệp duy nhất; copytree sẽ không.


2
docs.python.org/library/shutil.html bao gồm mã cho copytree () thể hiện việc xử lý các tệp, liên kết tượng trưng và thư mục thông thường.
James Polley

1
Câu trả lời này không giải quyết câu hỏi. Nó phải là một bình luận, không phải là một câu trả lời.
Jean-François Corbett,


-2

Phương thức python shutil.copytree là một mớ hỗn độn. Tôi đã thực hiện một cách hoạt động chính xác:

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
Mã này phù hợp cho công việc kiểm tra tệp cá nhân (kiểm tra vấn đề ghi đè), nhưng sẽ không hoạt động đối với các tệp nhị phân như 'zip'. Tại sao không sử dụng sao chép tệp python đơn giản thay vì đọc / ghi từng dòng?
notilas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.