While Python provides some helpful methods for moving files and
directories around (shutil.move, shutil.copytree, etc), none of
them seem to be able to merge two directories. -snip-
Any ideas?
It's not pretty, but you could hack up the original copytree()
source so it ignores errors from the makedirs() call. Then it
should work more-or-less like 'cp -r'. This isn't ideal, because
'OSError' is thrown, which could mean that the dir already exists
(okay), or it could be a 'real' error, like the current user doesn't
have permission to write to the destination. (See 'XXX' in the code
below.)
Better would be to match on the error string and only ignore the
'directory exists' error. Unfortunately, the error messages do
vary per-platform. Here's what I see under Windows when the
directory already exists:
WindowsError: [Error 183] Cannot create a file when that file
already exists: 'test2'
and under CentOS:
OSError: [Errno 17] File exists: 'test2'
The code below seems to work under both Linux and Windows; but I
didn't really test it much, so handle with care. :-}
----------
def copytree(src, dst, symlinks=False, ignore=None):
import os
from shutil import copy2, copystat, Error
names = os.listdir(src)
if ignore is not None:
ignored_names = ignore(src, names)
else:
ignored_names = set()
try:
os.makedirs(dst)
except OSError, exc:
# XXX - this is pretty ugly
if "file already exists" in exc[1]: # Windows
pass
elif "File exists" in exc[1]: # Linux
pass
else:
raise
errors = []
for name in names:
if name in ignored_names:
continue
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
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:
copy2(srcname, dstname)
# XXX What about devices, sockets etc.?
except (IOError, os.error), why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except Error, err:
errors.extend(err.args[0])
try:
copystat(src, dst)
except WindowsError:
# can't copy file access times on Windows
pass
except OSError, why:
errors.extend((src, dst, str(why)))
if errors:
raise Error, errors