修改python中的符号链接

问题描述 投票:26回答:7

如何在Python中将符号链接更改为从一个文件指向另一个文件?

os.symlink函数似乎只能用于创建新的符号链接。

python symlink
7个回答
23
投票

如果您需要原子修改,则取消链接将不起作用。

更好的解决方案是创建一个新的临时符号链接,然后将其重命名为现有符号链接:

os.symlink(target, tmpLink)
os.rename(tmpLink, linkName)

您可以检查以确保它也已正确更新:

if os.path.realpath(linkName) == target:
    # Symlink was updated

根据os.rename的文档,可能无法在Windows中自动更改符号链接。在这种情况下,您只需删除并重新创建。


23
投票

Python2的一个小功能,尝试符号链接,如果由于现有文件而失败,它会删除它并再次链接。检查Tom Hale's answer以获取最新的解决方案。

import os, errno

def symlink_force(target, link_name):
    try:
        os.symlink(target, link_name)
    except OSError, e:
        if e.errno == errno.EEXIST:
            os.remove(link_name)
            os.symlink(target, link_name)
        else:
            raise e

9
投票

您可以首先使用os.unlink(),然后使用os.symlink()重新创建指向新目标。


6
投票

我最近研究了这个问题,并发现最好的方法确实是unlink然后symlink。但是,如果您只需要修复损坏的链接,例如使用自动替换,那么您可以执行os.readlink

for f in os.listdir(dir):
    path = os.path.join(dir, f)
    old_link = os.readlink(path)
    new_link = old_link.replace(before, after)
    os.unlink(path)
    os.symlink(new_link, path)

2
投票

给定overwrite=True,此函数将使用符号链接安全地覆盖现有文件。

它认识到种族条件,这就是为什么它不短,但它是安全的。

import os, tempfile

def symlink(target, link_name, overwrite=False):
    '''
    Create a symbolic link named link_name pointing to target.
    If link_name exists then FileExistsError is raised, unless overwrite=True.
    When trying to overwrite a directory, IsADirectoryError is raised.
    '''

    if not overwrite:
        os.symlink(target, linkname)
        return

    # os.replace() may fail if files are on different filesystems
    link_dir = os.path.dirname(link_name)

    # Create link to target with temporary filename
    while True:
        temp_link_name = tempfile.mktemp(dir=link_dir)

        # os.* functions mimic as closely as possible system functions
        # The POSIX symlink() returns EEXIST if link_name already exists
        # https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html
        try:
            os.symlink(target, temp_link_name)
            break
        except FileExistsError:
            pass

    # Replace link_name with temp_link_name
    try:
        # Pre-empt os.replace on a directory with a nicer message
        if os.path.isdir(link_name):
            raise IsADirectoryError(f"Cannot symlink over existing directory: '{link_name}'")
        os.replace(temp_link_name, link_name)
    except:
        if os.path.islink(temp_link_name):
            os.remove(temp_link_name)
        raise

学生笔记:

  1. 如果函数失败(例如计算机崩溃),则可能存在到目标的附加随机链接。
  2. 一个不太可能的竞争条件仍然存在:在替换temp_link_name之前,在随机命名的link_name上创建的符号链接可以被另一个进程修改。

我提出了一个python issue,以突出os.symlink()问题,要求目标不存在,我被建议提高my suggestion on the python-ideas mailing list

感谢Robert Siemer’s input


1
投票

在e.errno!= errno.EEXIST的情况下,不要忘记添加raise命令。您不想隐藏错误:

if e.errno == errno.EEXIST:
     os.remove(link_name)
     os.symlink(target, link_name)
else:
    raise

0
投票

一个快速简便的解决方案:

while True:
     try:
         os.symlink(target, link_name)
         break
     except FileExistsError:
         os.remove(link_name)

但是,当替换应该始终存在的符号链接时,这会有竞争条件,例如:

 /lib/critical.so -> /lib/critical.so.1.2

升级时:

 my_symlink('/lib/critical.so.2.0', '/lib/critical.so')

有一个时间点/lib/critical.so不存在。

This answer避免了竞争条件。

© www.soinside.com 2019 - 2024. All rights reserved.