Изменение символической ссылки в Python - PullRequest
26 голосов
/ 28 ноября 2011

Как изменить символическую ссылку, чтобы она указывала из одного файла в другой в Python?

Функция os.symlink, похоже, работает только для создания новых символических ссылок.

Ответы [ 7 ]

23 голосов
/ 06 января 2015

Если вам нужна атомарная модификация, отсоединение не будет работать.

Лучшим решением было бы создать новую временную символическую ссылку, а затем переименовать ее поверх существующей:

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

Вы также можете убедиться, что оно обновлено правильно:

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

В соответствии с документацией для os.rename, однако, не может быть способа атомарно изменить символическую ссылку в Windows.В этом случае вы просто удалите и заново создадите.

23 голосов
/ 24 ноября 2014

Небольшая функция для Python2, которая пытается создать символическую ссылку, и, если она не работает из-за существующего файла, она удаляет ее и снова связывает. Проверьте ответ Тома Хейла на наличие актуального решения.

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 голосов
/ 28 ноября 2011

Вы можете os.unlink() сначала, а затем заново создать, используя os.symlink(), чтобы указать на новую цель.

6 голосов
/ 28 ноября 2011

Я недавно исследовал этот вопрос и обнаружил, что лучший способ - это 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)
3 голосов
/ 18 апреля 2019

Учитывая 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.

Я поднял проблему с питоном , чтобы осветить проблемы os.symlink(), требующие, чтобы цель не существовала, где мне посоветовали поднять мое предложение в python-ideas списке рассылки

Кредит Вход Роберта Симера .

1 голос
/ 24 августа 2015

Не забудьте добавить команду повышения в случае, когда e.errno! = Errno.EEXIST. Тогда вы не хотите скрывать ошибку:

if e.errno == errno.EEXIST:
     os.remove(link_name)
     os.symlink(target, link_name)
else:
    raise
0 голосов
/ 13 мая 2019

Быстрое и простое решение:

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 не существует.

Этот ответ избегаетсостояние гонки.

...