Copying a symbolic link in Python
I want to copy a file src
to the destination dst
, but if src
happens to be a symbolic link, preserve the link instead of copying the contents of the file. After the copy is performed, os.readlink
should return the same for both src
and dst
.
The module shutil
has several functions, such as copyfile
, copy
and copy2
, but all of these will copy the contents of the file, and w开发者_开发技巧ill not preserve the link. shutil.move
has the correct behavior, other than the fact it removes the original file.
Is there a built-in way in Python to perform a file copy while preserving symlinks?
Just do
def copy(src, dst):
if os.path.islink(src):
linkto = os.readlink(src)
os.symlink(linkto, dst)
else:
shutil.copy(src,dst)
shutil.copytree does something similar, but as senderle noted, it's picky about copying only directories, not single files.
Python 3 follow_symlinks
In Python 3, most copy methods of shutil
have learned the follow_symlinks
argument, which preserves symlinks if selected.
E.g. for shutil.copy
:
shutil.copy(src, dest, follow_symlinks=False)
and the docs say:
shutil.copy(src, dst, *, follow_symlinks=True)
Copies the file src to the file or directory dst. src and dst should be strings. If dst specifies a directory, the file will be copied into dst using the base filename from src. Returns the path to the newly created file.
If
follow_symlinks
is false, and src is a symbolic link, dst will be created as a symbolic link. If follow_symlinks` is true and src is a symbolic link, dst will be a copy of the file src refers to.
This has one problem however: if you try to overwrite an existing file or symlink, it fails with:
FileExistsError: [Errno 17] File exists: 'b' -> 'c'
unlike the follow_symlinks=True
which successfully overwrites.
The same also happens for os.symlink
, so I ended up using instead:
#!/usr/bin/env python3
import shutil
import os
def copy(src, dst):
if os.path.islink(src):
if os.path.lexists(dst):
os.unlink(dst)
linkto = os.readlink(src)
os.symlink(linkto, dst)
else:
shutil.copy(src, dst)
if __name__ == '__main__':
os.symlink('c', 'b')
os.symlink('b', 'a')
copy('a', 'b')
with open('c', 'w') as f:
f.write('a')
with open('d', 'w'):
pass
copy('c', 'd')
copy('a', 'c')
Tested in Ubuntu 18.10, Python 3.6.7.
精彩评论