在Python中复制符号链接

33 投票
2 回答
30462 浏览
提问于 2025-04-16 10:55

我想把一个文件 src 复制到目标 dst,但是如果 src 是一个符号链接的话,我希望保留这个链接,而不是复制文件的内容。复制完成后,使用 os.readlink 应该能返回相同的结果给 srcdst

shutil 模块里,有几个函数,比如 copyfilecopycopy2,但这些函数都会复制文件的 内容,而不会保留链接。虽然 shutil.move 的行为是正确的,但它会删除原来的文件。

在 Python 中,有没有内置的方法可以在复制文件时保留符号链接呢?

2 个回答

48

只需要这样做

def copy(src, dst):
    if os.path.islink(src):
        linkto = os.readlink(src)
        os.symlink(linkto, dst)
    else:
        shutil.copy(src,dst)

shutil.copytree 是一个类似的功能,但正如senderle提到的,它只会复制文件夹,而不会单独复制文件。

13

Python 3 中的 follow_symlinks

在 Python 3 中,shutil 里的大部分复制方法都增加了一个叫 follow_symlinks 的参数,这个参数可以选择保留符号链接。

比如说,对于 shutil.copy

shutil.copy(src, dest, follow_symlinks=False)

而且文档中说

shutil.copy(src, dst, *, follow_symlinks=True)

这个方法会把源文件 src 复制到目标文件或目录 dst。src 和 dst 应该是字符串。如果 dst 指定的是一个目录,那么文件会用 src 的基本文件名复制到 dst 中。返回的是新创建文件的路径。

如果 follow_symlinks 设置为 false,并且 src 是一个符号链接,那么 dst 会被创建为一个符号链接。如果 follow_symlinks 设置为 true,并且 src 是一个符号链接,那么 dst 会是 src 指向的文件的副本。

不过,这里有一个问题:如果你尝试覆盖一个已经存在的文件或符号链接,会失败,并显示:

FileExistsError: [Errno 17] File exists: 'b' -> 'c'

而如果 follow_symlinks=True,就能成功覆盖。

同样的情况也发生在 os.symlink 上,所以我最后选择使用:

#!/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')

在 Ubuntu 18.10 和 Python 3.6.7 中测试过。

撰写回答