Изменение символической ссылки в python
Как мне изменить как символическую ссылку, чтобы указать из одного файла в другой в Python?
Функция os.symlink
работает только для создания новых символических ссылок.
Ответы
Ответ 1
Небольшая функция для 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
Ответ 2
Если вам нужна атомная модификация, отмена не будет работать.
Лучшим решением было бы создать новую временную символическую ссылку, а затем переименовать ее поверх существующей:
os.symlink(target, tmpLink)
os.rename(tmpLink, linkName)
Вы также можете проверить, правильно ли он был обновлен:
if os.path.realpath(linkName) == target:
# Symlink was updated
В соответствии с документацией для os.rename, хотя, возможно, нет возможности атомизировать изменение символической ссылки в Windows. В этом случае вы просто удалите и заново создадите.
Ответ 3
Вы могли os.unlink()
сначала, а затем повторно создать с помощью os.symlink()
, чтобы указать к новой цели.
Ответ 4
Недавно я исследовал этот вопрос и выяснил, что наилучшим способом является 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)
Ответ 5
Учитывая 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
Примечания для педантов:
-
В случае сбоя функции (например, сбой компьютера) может существовать дополнительная случайная ссылка на цель.
-
Условие маловероятного temp_link_name
все еще остается: символическая ссылка, созданная со случайным именем temp_link_name
может быть изменена другим процессом до замены link_name
.
Я поднял проблему с питоном, чтобы осветить проблемы os.symlink()
требующих, чтобы цель не существовала, где мне посоветовали поднять мое предложение в списке рассылки python-ideas
Благодарим Роберта Симерса за вклад.
Ответ 6
Не забудьте добавить команду raise в случае, когда e.errno!= errno.EEXIST. Вы не хотите скрывать ошибку:
if e.errno == errno.EEXIST:
os.remove(link_name)
os.symlink(target, link_name)
else:
raise
Ответ 7
Быстрое и простое решение:
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
не существует.
Этот ответ позволяет избежать состояния гонки.