2010-01-03 26 views
91

Python parece tener funciones para copiar archivos (por ejemplo, shutil.copy) y funciones para copiar directorios (por ejemplo, shutil.copytree) pero no he encontrado ninguna función que maneje ambos. Claro, es trivial comprobar si desea copiar un archivo o un directorio, pero parece una omisión extraña.Copiar archivos o directorios recursivamente en Python

¿Realmente no hay una función estándar que funcione como el comando unix cp -r, es decir, admite tanto directorios como archivos y copias de forma recursiva? ¿Cuál sería la forma más elegante de solucionar este problema en Python?

+3

Sí, esto es un desastre. Uno de los lugares donde, al tratar de reflejar las llamadas al sistema subyacentes, Python empeora la interfaz visible. Aunque no es difícil cambiar entre copy-file y copy-tree, no debería haber sido necesario. ¿Tal vez presentar una solicitud de mejora en el rastreador de errores de Python para permitir que 'copytree' copie un solo archivo? – bobince

Respuesta

109

Le sugiero que llame primero al shutil.copytree, y si se lanza una excepción, vuelva a intentar con 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

Creo que sería mucho más limpio simplemente comprobar si src es un directorio que utiliza os.path.isdir (src) en lugar de atrapar una excepción como esta. ¿O hay alguna razón especial por la que uno debería usar una excepción aquí? – pafcu

+23

1) Porque en el mundo de Python EAFP (es más fácil pedir perdón que permiso) es preferible a LBYL (mira antes de saltar).Puedo proporcionarle enlaces sobre eso, si le parece nuevo. 2) La función de la biblioteca ya lo verifica indirectamente, ¿por qué replicar el cheque? 3) nada impide que la función 'shutil.copytree' mejore y administre ambos casos en el futuro. 4) Las excepciones no son tan excepcionales en Python; p.ej. una iteración se detiene lanzando una excepción StopIteration. – tzot

+2

Bueno, en este caso, manejar la excepción toma 6 líneas, mientras que verificar el tipo toma 4 líneas. No mucho, pero se suma al final. Además, como dices, copytree podría algún día ser compatible también con archivos. Pero es imposible saber cómo será esa implementación. ¿Tal vez arroja una excepción bajo alguna circunstancia donde la copia funciona? En ese caso, mi código dejaría de funcionar repentinamente solo por la funcionalidad añadida. Pero probablemente tenga razón, las excepciones son bastante comunes en Python, algo que me parece muy molesto, pero es probable que sea porque nunca me acostumbro – pafcu

3

Unix cp no 'apoyar ambos directorios y archivos':

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

Para hacer cp copiar un directorio, usted tiene que contar manualmente cp que se trata de un directorio, mediante el uso de la bandera '-r' .

Sin embargo, aquí hay algo de desconexión: cp -r cuando se pasa un nombre de archivo, ya que la fuente copiará felizmente solo el archivo; copytree no lo hará.

+2

http://docs.python.org/library/shutil.html incluye el código para copytree() que demuestra el manejo de archivos comunes, enlaces simbólicos y directorios. –

+1

Esta respuesta no aborda la pregunta. Debe ser un comentario, no una respuesta. –

-1

El método python shutil.copytree es un desastre. He hecho uno que funcione correctamente:

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

Este código es bueno para el trabajo de verificación de archivos individuales (verifique el problema de sobrescritura), pero no funcionará para archivos binarios como 'zip'. ¿Por qué no utilizar una copia de archivo python simple en lugar de lectura/escritura línea por línea? – notilas

1

shutil.copy y shutil.copy2 están copiando los archivos.

shutil.copytree copia una carpeta con todos los archivos y todas las subcarpetas. shutil.copytree está usando shutil.copy2 para copiar los archivos.

Así que el análogo a cp -r que está diciendo es la shutil.copytree PORQUE cp -r objetivos y copia una carpeta y sus archivos/subcarpetas como shutil.copytree. Sin el -rcp copia archivos como shutil.copy y shutil.copy2 do.

+0

No creo que haya entendido la pregunta. Pruebe 'shutil.copytree ('C: \ myfile.txt', 'C: \ otherfile')'. No funciona Eso es lo que el OP estaba preguntando ... hace 7 años. –

+0

Por supuesto que no funciona. Al igual que cp no funciona con carpetas. Necesitas una opción especial. copiar y copiar árbol son la mejor manera de manejar la copia. Si copytree pudiera apuntar y archivar, sería fácil cometer errores. Debe saber a qué se dirige tanto con Linux como con Python. Así de dificil. Además, alguien más lo comentó aquí, pero al ver la pregunta y las respuestas no pudo resistirse a responder. La manera elegante es saber lo que quieres hacer y no una copia universal sin ningún control. – gms

3

Para agregar Tzot's y gns respuestas, aquí hay una forma alternativa de copiar archivos y carpetas recursivamente. (3.X Python)

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) 

caso de que sea la primera vez y no tiene idea de cómo copiar archivos y carpetas de forma recursiva, espero que esto ayude.

Cuestiones relacionadas