2010-01-03 56 views
91

Python似乎具有複製文件的功能(例如shutil.copy)和用於複製目錄的功能(例如shutil.copytree),但是我還沒有找到處理兩者的函數。當然,檢查你是否想要複製一個文件或一個目錄很簡單,但這看起來像是一個奇怪的遺漏。在Python中遞歸複製文件或目錄

是否真的沒有像unix cp -r命令一樣工作的標準功能,即支持目錄和文件以及遞歸複製?在Python中解決這個問題的最優雅方式是什麼?

+3

是的,這是一團糟。其中一個地方,通過試圖反映底層的系統調用,Python使可見接口變得更糟。雖然在複製文件和複製樹之間切換並不困難,但它不應該是必需的。也許可以在Python bug跟蹤器上提出增強請求,以允許'copytree'複製單個文件? – bobince 2010-01-03 12:35:25

回答

109

我建議你先打電話shutil.copytree,如果拋出異常,則用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 
+13

我認爲簡單地檢查src是否是使用os.path.isdir(src)的目錄而不是像這樣捕獲異常會更簡潔。或者有什麼特別的理由應該在這裏使用異常呢? – pafcu 2010-01-03 13:09:44

+23

1)因爲在Python世界中EAFP(比請求寬鬆要求寬鬆)比LBYL更優先(在你跳躍之前)。如果聽起來對你而言是新鮮的,我可以向你提供有關該鏈接的鏈接。 2)庫函數已經間接檢查了,爲什麼要複製這個檢查? 3)沒有什麼能阻止'shutil.copytree'函數在將來改進和管理這兩種情況。 4)Python中的例外情況並不特別;例如通過拋出StopIteration異常停止迭代。 – tzot 2010-01-03 20:27:58

+2

那麼,在這種情況下處理異常需要6行,而檢查類型需要4行。沒有多少,但它最終加起來。另外,正如你所說,copytree可能有一天會很好地支持文件。但是不可能說出那個實現會是什麼樣子。也許它會在複製工作的某些情況下引發異常?在那種情況下,我的代碼會因爲增加的功能而突然停止工作。 但是你可能是對的,Python中的異常是相當通用的,我覺得很煩人,但這可能是因爲我似乎從來沒有習慣它 – pafcu 2010-01-04 14:18:23

3

的Unix cp不「支持目錄和文件」:

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

爲了使CP複製目錄,你必須手動告訴CP,這是一個目錄,通過使用「-r」標誌。

儘管有一些斷開連接 - cp -r當傳遞一個文件名作爲源將愉快地複製單個文件; copytree不會。

+2

http://docs.python.org/library/shutil.html包含用於演示處理普通文件,符號鏈接和目錄的copytree()代碼。 – 2010-01-03 10:28:16

+1

這個答案沒有解決這個問題。它應該是一個評論,而不是一個答案。 – 2017-02-15 11:51:37

-1

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 2016-09-28 15:52:54

1

shutil.copyshutil.copy2被複制文件。

shutil.copytree複製一個包含所有文件和所有子文件夾的文件夾。 shutil.copytree正在使用shutil.copy2複製文件。

所以模擬cp -r你說的是shutil.copytree因爲cp -r目標和拷貝一個文件夾,並像shutil.copytree它的文件/子文件夾。沒有-rcp副本文件,如shutil.copyshutil.copy2

+0

我認爲你不瞭解這個問題。試試'shutil.copytree('C:\ myfile.txt','C:\ otherfile')'。它不起作用。這就是OP在7年前所問的。 – 2017-02-15 15:09:23

+0

當然,這是行不通的。像cp不適用於文件夾。你需要一個特殊的選擇。複製和副本樹是處理複製的最佳方式。如果copytree可以定位並存檔,那麼很容易出錯。你必須知道你在Linux和Python上都瞄準了什麼。那很難。再加上其他人在這裏評論它,但看到問題和答覆無法抗拒的答案。優雅的方式是知道你想做什麼,而不是沒有任何控制的通用副本。 – gms 2017-02-15 16:15:45

3

要添加到Tzot'sgns的答案,這裏是遞歸複製文件和文件夾的替代方法。 (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) 

它應該是你第一次和你不知道如何遞歸複製文件和文件夾,我希望這有助於。