Comment copier un répertoire complet de fichiers dans un répertoire existant en utilisant Python?
Exécutez le code suivant à partir d'un répertoire qui contient un répertoire nommé bar
(contenant un ou plusieurs fichiers) et un répertoire nommé baz
(contenant également un ou plusieurs fichiers). Assurez-vous qu'il n'y a pas de répertoire nommé foo
.
import shutil
shutil.copytree('bar', 'foo')
shutil.copytree('baz', 'foo')
Il échouera avec:
$ 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'
je veux que cela fonctionne de la même manière que si j'avais tapé:
$ mkdir foo
$ cp bar/* foo/
$ cp baz/* foo/
Dois-je utiliser shutil.copy()
pour copier chaque fichier dans baz
en foo
? (Après avoir déjà copié le contenu de "bar" dans "foo" avec shutil.copytree()
?) Ou est-il un moyen plus facile/mieux?
12 réponses
cette limitation de la norme shutil.copytree
semble arbitraire et ennuyeuse. Palliatif:
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)
notez qu'il n'est pas entièrement compatible avec la copie standard:
- il n'a pas d'honneur
symlinks
etignore
paramètres pour le répertoire racine de lasrc
l'arbre; - il n'élève pas
shutil.Error
pour des erreurs au niveau racine desrc
; - en cas d'erreurs lors de la copie d'un sous-arbre, il relèvera
shutil.Error
pour ce sous-arbre au lieu d'essayer de copier d'autres sous-arbres et de soulever simple combinéshutil.Error
.
Voici une solution qui fait partie de la bibliothèque standard.
from distutils.dir_util import copy_tree
copy_tree("/a/b/c", "/x/y/z")
voir la même question.
copier le contenu du répertoire dans un répertoire avec python
en légère amélioration sur la réponse d'atzz à la fonction où la fonction ci-dessus tente toujours de copier les fichiers de la source à la destination.
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)
Dans mon ci-dessus mise en œuvre
- Création du répertoire de sortie s'il n'existe pas déjà
- faire le répertoire de copie en appelant récursivement ma propre méthode.
- quand on arrive à copier le fichier je vérifie si le fichier est modifié seulement nous devrions copier.
j'utilise la fonction ci-dessus avec SCons build. Il m'a beaucoup aidé, comme à chaque fois quand je compile je n'avez pas besoin de copier l'ensemble des fichiers.. mais seulement les fichiers qui sont modifiés.
Une fusion de l'un inspiré par atzz et 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)
- même comportement que shutil.copytree , avec symlinks et ignorer paramètres
- Créer le répertoire de destination de la structure si non existant
- ne manqueront pas si heure d'été existe déjà
docs d'indiquer explicitement que le répertoire de destination doit pas exister :
le répertoire de destination, nommé par
dst
, ne doit pas déjà exister; il sera créé ainsi que les répertoires parents manquants.
je pense que votre meilleur pari est de os.walk
le deuxième et tous les répertoires conséquents, copy2
répertoire et fichiers et faire copystat
supplémentaire pour les répertoires. Après tout c'est précisément ce que copytree
, comme expliqué dans les docs. Ou vous pouvez copy
et copystat
chaque répertoire/fichier et os.listdir
au lieu de os.walk
.
vous pouvez modifier shutil
et obtenir l'effet (sur ma version de shutil
c'est en ligne 315
)
Changement
os.makedirs(dst)
à
os.makedirs(dst,exist_ok=True)
je suppose que la manière la plus rapide et la plus simple serait que python appelle les commandes système...
exemple..
import os
cmd = '<command line call>'
os.system(cmd)
Tar et gzip du répertoire.... décompressez-le et décompresser le répertoire à l'endroit désiré.
yah?
c'est inspiré de la meilleure réponse originale fournie par atzz, je viens d'ajouter remplacer fichier / dossier logique. Donc il ne fusionne pas réellement, mais supprime le fichier/ dossier existant et copie le nouveau:
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)
désactiver la fonction rmtree pour en faire une fonction move.
Voici ma version de la même tâche::
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)
Voici une version inspirée de ce fil qui imite plus étroitement distutils.file_util.copy_file
.
updateonly
est un bool si True, ne copiera que les fichiers avec des dates modifiées plus récentes que les fichiers existants dans dst
à moins qu'ils ne soient listés dans forceupdate
qui copiera indépendamment.
ignore
et forceupdate
s'attendre à des listes de noms de fichier ou de dossier/nom par rapport à src
et d'accepter de style Unix caractères génériques similaire à glob
ou fnmatch
.
la fonction renvoie une liste de fichiers copiés (ou copiés si dryrun
si vrai).
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
la solution précédente pose un problème, à savoir que src
peut remplacer dst
sans aucune notification ou exception.
j'ajoute un predict_error
méthode pour prédire les erreurs avant la copie. copytree
principalement basé sur la version de Cyrille Pontvieux.
L'utilisation de predict_error
pour prédire toutes les erreurs au début est la meilleure, à moins que vous aimiez voir l'exception soulevée l'une par l'autre lors de l'exécution copytree
jusqu'à corriger toute erreur.
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)
Voici mon laissez-passer pour le problème. J'ai modifié le code source de copytree pour conserver la fonctionnalité originale, mais maintenant aucune erreur ne se produit lorsque le répertoire existe déjà. Je l'ai aussi modifié pour qu'il n'écrase pas les fichiers existants, mais conserve les deux copies, l'une avec un nom modifié, puisque c'était important pour mon application.
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)