Copying a symbolic link in Python

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.


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.

Tags:

Python

Symlink