简体   繁体   English

在python中修改符号链接

[英]Modifying a symlink in python

How do I change a symlink to point from one file to another in Python?如何在 Python 中更改符号链接以从一个文件指向另一个文件?

The os.symlink function only seems to work to create new symlinks. os.symlink函数似乎只能用于创建新的符号链接。

If you need an atomic modification, unlinking won't work.如果您需要原子修改,则取消链接将不起作用。

A better solution would be to create a new temporary symlink, and then rename it over the existing one:更好的解决方案是创建一个新的临时符号链接,然后将其重命名为现有符号链接:

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

You can check to make sure it was updated correctly too:您可以检查以确保它也已正确更新:

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

According to the documentation for os.rename though, there may be no way to atomically change a symlink in Windows.但是,根据 os.rename 的文档,可能无法在 Windows 中自动更改符号链接。 In that case, you would just delete and re-create.在这种情况下,您只需删除并重新创建。

A little function for Python2 which tries to symlink and if it fails because of an existing file, it removes it and links again. Python2 的一个小函数,它尝试符号链接,如果由于现有文件而失败,它将删除它并再次链接。 Check [Tom Hale's answer] for a up-to-date solution.检查 [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

For python3 except condition should be except OSError as e:对于 python3, except条件应该是except OSError as e:
[Tom Hale's answer]: https://stackoverflow.com/a/55742015/825924 【汤姆黑尔的回答】: https : //stackoverflow.com/a/55742015/825924

Given overwrite=True , this function will safely overwrite an existing file with a symlink.鉴于overwrite=True ,此函数将安全地使用符号链接覆盖现有文件。

It is cognisant of race conditions, which is why it is not short, but it is safe.它认识到竞争条件,这就是为什么它不短,但它是安全的。

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, link_name)
        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 not os.path.islink(link_name) and 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

Notes for pedants:学徒注意事项:

  1. If the function fails (eg computer crashes), an additional random link to the target might exist.如果该功能失败(例如计算机崩溃),则可能存在到目标的附加随机链接。

  2. An unlikely race condition still remains: the symlink created at the randomly-named temp_link_name could be modified by another process before replacing link_name .一个不太可能的竞争条件仍然存在:在随机命名的temp_link_name处创建的符号链接可以在替换link_name之前被另一个进程修改。

I raised a python issue to highlight the issues of os.symlink() requiring the target to not exist, where I was advised to raise my suggestion on the python-ideas mailing list我提出了一个python 问题来突出os.symlink()要求目标不存在的问题,在那里我被建议在python-ideas邮件列表中提出我的建议

Credit to Robert Siemer's input .归功于 Robert Siemer 的输入

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

I researched this question recently, and found out that the best way is indeed to unlink and then symlink .我最近研究了这个问题,发现最好的方法确实是unlink然后symlink But if you need just to fix broken links, for example with auto-replace, then you can do os.readlink :但是,如果您只需要修复损坏的链接,例如使用自动替换,那么您可以执行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)

Don't forget to add a raise command in the case when e.errno != errno.EEXIST You don't want to hide an error then:不要忘记在 e.errno != errno.EEXIST 的情况下添加 raise 命令,然后您不想隐藏错误:

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

A quick and easy solution:一个快速简便的解决方案:

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

However this has a race condition when replacing a symlink which should always exist, eg:但是,在替换应始终存在的符号链接时,这存在竞争条件,例如:

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

When upgrading by:通过以下方式升级时:

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

There is a point in time when /lib/critical.so doesn't exist.有一个时间点/lib/critical.so不存在。

This answer avoids the race condition.这个答案避免了竞争条件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM