varias - python recorrer archivos de una carpeta
¿Cómo copio un directorio completo de archivos en un directorio existente usando Python? (12)
Aquí está mi pase al problema. Modifiqué el código fuente de copytree para mantener la funcionalidad original, pero ahora no ocurre ningún error cuando el directorio ya existe. También lo cambié para que no sobrescriba los archivos existentes, sino que guarda ambas copias, una con un nombre modificado, ya que esto era importante para mi aplicación.
import shutil
import os
def _copytree(src, dst, symlinks=False, ignore=None):
"""
This is an improved version of shutil.copytree which allows writing to
existing folders and does not overwrite existing files but instead appends
a ~1 to the file name and adds it to the destination path.
"""
names = os.listdir(src)
if ignore is not None:
ignored_names = ignore(src, names)
else:
ignored_names = set()
if not os.path.exists(dst):
os.makedirs(dst)
shutil.copystat(src, dst)
errors = []
for name in names:
if name in ignored_names:
continue
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
i = 1
while os.path.exists(dstname) and not os.path.isdir(dstname):
parts = name.split(''.'')
file_name = ''''
file_extension = parts[-1]
# make a new file name inserting ~1 between name and extension
for j in range(len(parts)-1):
file_name += parts[j]
if j < len(parts)-2:
file_name += ''.''
suffix = file_name + ''~'' + str(i) + ''.'' + file_extension
dstname = os.path.join(dst, suffix)
i+=1
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
elif os.path.isdir(srcname):
_copytree(srcname, dstname, symlinks, ignore)
else:
shutil.copy2(srcname, dstname)
except (IOError, os.error) as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except BaseException as err:
errors.extend(err.args[0])
try:
shutil.copystat(src, dst)
except WindowsError:
# can''t copy file access times on Windows
pass
except OSError as why:
errors.extend((src, dst, str(why)))
if errors:
raise BaseException(errors)
Ejecute el siguiente código desde un directorio que contiene un directorio llamado bar
(que contiene uno o más archivos) y un directorio llamado baz
(que también contiene uno o más archivos). Asegúrese de que no haya un directorio llamado foo
.
import shutil
shutil.copytree(''bar'', ''foo'')
shutil.copytree(''baz'', ''foo'')
Fallará con:
$ python copytree_test.py
Traceback (most recent call last):
File "copytree_test.py", line 5, in <module>
shutil.copytree(''baz'', ''foo'')
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/shutil.py", line 110, in copytree
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/os.py", line 172, in makedirs
OSError: [Errno 17] File exists: ''foo''
Quiero que esto funcione de la misma manera que si hubiera escrito:
$ mkdir foo
$ cp bar/* foo/
$ cp baz/* foo/
¿Debo usar shutil.copy()
para copiar cada archivo en baz
en foo
? (Después de que ya he copiado los contenidos de ''bar'' en ''foo'' con shutil.copytree()
?) ¿O hay una manera más fácil / mejor?
Aquí está mi versión de la misma tarea ::
import os, glob, shutil
def make_dir(path):
if not os.path.isdir(path):
os.mkdir(path)
def copy_dir(source_item, destination_item):
if os.path.isdir(source_item):
make_dir(destination_item)
sub_items = glob.glob(source_item + ''/*'')
for sub_item in sub_items:
copy_dir(sub_item, destination_item + ''/'' + sub_item.split(''/'')[-1])
else:
shutil.copy(source_item, destination_item)
Aquí hay una solución que es parte de la biblioteca estándar.
from distutils.dir_util import copy_tree
copy_tree("/a/b/c", "/x/y/z")
Ver esta pregunta similar.
Copie los contenidos del directorio en un directorio con python
Aquí hay una versión inspirada en este hilo que imita más de cerca a distutils.file_util.copy_file
.
updateonly
es un bool si es verdadero, solo copiará los archivos con fechas modificadas más nuevos que los archivos existentes en dst
menos que estén listados en forceupdate
que se copiarán independientemente.
ignore
y forceupdate
esperan listas de nombres de archivos o carpetas / archivos relativos a src
y aceptan comodines de estilo Unix similares a glob
o fnmatch
.
La función devuelve una lista de archivos copiados (o se copiará si dryrun
si es True).
import os
import shutil
import fnmatch
import stat
import itertools
def copyToDir(src, dst, updateonly=True, symlinks=True, ignore=None, forceupdate=None, dryrun=False):
def copySymLink(srclink, destlink):
if os.path.lexists(destlink):
os.remove(destlink)
os.symlink(os.readlink(srclink), destlink)
try:
st = os.lstat(srclink)
mode = stat.S_IMODE(st.st_mode)
os.lchmod(destlink, mode)
except OSError:
pass # lchmod not available
fc = []
if not os.path.exists(dst) and not dryrun:
os.makedirs(dst)
shutil.copystat(src, dst)
if ignore is not None:
ignorepatterns = [os.path.join(src, *x.split(''/'')) for x in ignore]
else:
ignorepatterns = []
if forceupdate is not None:
forceupdatepatterns = [os.path.join(src, *x.split(''/'')) for x in forceupdate]
else:
forceupdatepatterns = []
srclen = len(src)
for root, dirs, files in os.walk(src):
fullsrcfiles = [os.path.join(root, x) for x in files]
t = root[srclen+1:]
dstroot = os.path.join(dst, t)
fulldstfiles = [os.path.join(dstroot, x) for x in files]
excludefiles = list(itertools.chain.from_iterable([fnmatch.filter(fullsrcfiles, pattern) for pattern in ignorepatterns]))
forceupdatefiles = list(itertools.chain.from_iterable([fnmatch.filter(fullsrcfiles, pattern) for pattern in forceupdatepatterns]))
for directory in dirs:
fullsrcdir = os.path.join(src, directory)
fulldstdir = os.path.join(dstroot, directory)
if os.path.islink(fullsrcdir):
if symlinks and dryrun is False:
copySymLink(fullsrcdir, fulldstdir)
else:
if not os.path.exists(directory) and dryrun is False:
os.makedirs(os.path.join(dst, dir))
shutil.copystat(src, dst)
for s,d in zip(fullsrcfiles, fulldstfiles):
if s not in excludefiles:
if updateonly:
go = False
if os.path.isfile(d):
srcdate = os.stat(s).st_mtime
dstdate = os.stat(d).st_mtime
if srcdate > dstdate:
go = True
else:
go = True
if s in forceupdatefiles:
go = True
if go is True:
fc.append(d)
if not dryrun:
if os.path.islink(s) and symlinks is True:
copySymLink(s, d)
else:
shutil.copy2(s, d)
else:
fc.append(d)
if not dryrun:
if os.path.islink(s) and symlinks is True:
copySymLink(s, d)
else:
shutil.copy2(s, d)
return fc
En una ligera mejora en la respuesta de atzz a la función donde la función anterior siempre intenta copiar los archivos de origen a destino.
def copytree(src, dst, symlinks=False, ignore=None):
if not os.path.exists(dst):
os.makedirs(dst)
for item in os.listdir(src):
s = os.path.join(src, item)
d = os.path.join(dst, item)
if os.path.isdir(s):
copytree(s, d, symlinks, ignore)
else:
if not os.path.exists(d) or os.stat(s).st_mtime - os.stat(d).st_mtime > 1:
shutil.copy2(s, d)
En mi implementación anterior
- Crear el directorio de salida si aún no existe
- Hacer el directorio de copia llamando recursivamente a mi propio método.
- Cuando lleguemos a copiar realmente el archivo, verifico si el archivo está modificado, entonces solo deberíamos copiar.
Estoy usando la función anterior junto con la construcción de scons. Me ayudó mucho, ya que cada vez que compilo puede que no necesite copiar todo el conjunto de archivos ... pero solo los archivos que se modifican.
Esta limitación del shutil.copytree
estándar parece arbitraria y molesta. Solución:
def copytree(src, dst, symlinks=False, ignore=None):
for item in os.listdir(src):
s = os.path.join(src, item)
d = os.path.join(dst, item)
if os.path.isdir(s):
shutil.copytree(s, d, symlinks, ignore)
else:
shutil.copy2(s, d)
Tenga en cuenta que no es del todo coherente con el copytree estándar:
- no
symlinks
eignore
parámetros del directorio raíz del árbolsrc
; - no levanta
shutil.Error
por errores en el nivel raíz desrc
; - en caso de errores durante la copia de un subárbol, levantará
shutil.Error
para ese subárbol en lugar de intentar copiar otros subárboles y generar un únicoshutil.Error
combinado.shutil.Error
.
Esto está inspirado en la mejor respuesta original proporcionada por atzz, acabo de agregar la lógica de reemplazar archivo / carpeta. Por lo tanto, en realidad no se fusiona, pero elimina el archivo / carpeta existente y copia el nuevo:
import shutil
import os
def copytree(src, dst, symlinks=False, ignore=None):
for item in os.listdir(src):
s = os.path.join(src, item)
d = os.path.join(dst, item)
if os.path.exists(d):
try:
shutil.rmtree(d)
except Exception as e:
print e
os.unlink(d)
if os.path.isdir(s):
shutil.copytree(s, d, symlinks, ignore)
else:
shutil.copy2(s, d)
#shutil.rmtree(src)
Descomenta el rmtree para convertirlo en una función de movimiento.
La solución anterior tiene un problema que src
puede sobreescribir dst
sin ninguna notificación o excepción.
Agrego un método predict_error
para predecir errores antes de la copia. copytree
principalmente en la versión de Cyrille Pontvieux.
El uso de predict_error
para predecir todos los errores al principio es lo mejor, a menos que desee ver la excepción copytree
por otra al ejecutar copytree
hasta que se corrija todo el error.
def predict_error(src, dst):
if os.path.exists(dst):
src_isdir = os.path.isdir(src)
dst_isdir = os.path.isdir(dst)
if src_isdir and dst_isdir:
pass
elif src_isdir and not dst_isdir:
yield {dst:''src is dir but dst is file.''}
elif not src_isdir and dst_isdir:
yield {dst:''src is file but dst is dir.''}
else:
yield {dst:''already exists a file with same name in dst''}
if os.path.isdir(src):
for item in os.listdir(src):
s = os.path.join(src, item)
d = os.path.join(dst, item)
for e in predict_error(s, d):
yield e
def copytree(src, dst, symlinks=False, ignore=None, overwrite=False):
''''''
would overwrite if src and dst are both file
but would not use folder overwrite file, or viceverse
''''''
if not overwrite:
errors = list(predict_error(src, dst))
if errors:
raise Exception(''copy would overwrite some file, error detail:%s'' % errors)
if not os.path.exists(dst):
os.makedirs(dst)
shutil.copystat(src, dst)
lst = os.listdir(src)
if ignore:
excl = ignore(src, lst)
lst = [x for x in lst if x not in excl]
for item in lst:
s = os.path.join(src, item)
d = os.path.join(dst, item)
if symlinks and os.path.islink(s):
if os.path.lexists(d):
os.remove(d)
os.symlink(os.readlink(s), d)
try:
st = os.lstat(s)
mode = stat.S_IMODE(st.st_mode)
os.lchmod(d, mode)
except:
pass # lchmod not available
elif os.path.isdir(s):
copytree(s, d, symlinks, ignore)
else:
if not overwrite:
if os.path.exists(d):
continue
shutil.copy2(s, d)
Puede modificar shutil
y obtener el efecto
Cambio
os.makedirs(dst)
A
os.makedirs(dst,exist_ok=True)
Una fusión inspirada por atzz y Mital Vora:
#!/usr/bin/python
import os
import shutil
import stat
def copytree(src, dst, symlinks = False, ignore = None):
if not os.path.exists(dst):
os.makedirs(dst)
shutil.copystat(src, dst)
lst = os.listdir(src)
if ignore:
excl = ignore(src, lst)
lst = [x for x in lst if x not in excl]
for item in lst:
s = os.path.join(src, item)
d = os.path.join(dst, item)
if symlinks and os.path.islink(s):
if os.path.lexists(d):
os.remove(d)
os.symlink(os.readlink(s), d)
try:
st = os.lstat(s)
mode = stat.S_IMODE(st.st_mode)
os.lchmod(d, mode)
except:
pass # lchmod not available
elif os.path.isdir(s):
copytree(s, d, symlinks, ignore)
else:
shutil.copy2(s, d)
- Mismo comportamiento que shutil.copytree , con enlaces simbólicos e ignorar parámetros
- Crear estructura de destino de directorio si no existe
- No fallará si ya existe el dst
supongo que la manera más rápida y simple sería hacer que python invoque los comandos del sistema ...
ejemplo..
import os
cmd = ''<command line call>''
os.system(cmd)
Tar y gzip en el directorio ... descomprime y descomprime el directorio en el lugar deseado.
yah?
los documentos establecen explícitamente que el directorio de destino no debería existir :
El directorio de destino, nombrado por
dst
, no debe existir ya; se creará así como los directorios principales faltantes.
Creo que su mejor os.walk
es hacer el siguiente y todos los directorios copy2
, el directorio y los archivos copystat
y hacer copystat
adicional para los directorios. Después de todo, eso es precisamente lo que copytree
hace como se explica en los documentos. O puede copy
y copy
copystat
cada directorio / archivo y os.listdir
lugar de os.walk
.