Pythonであるファイルから別のファイルを指すようにシンボリックリンクを変更するにはどうすればよいですか?
os.symlink
関数は新しいシンボリックリンクを作成するためにのみ機能するようです。
アトミックな変更が必要な場合、リンク解除は機能しません。
より良い解決策は、新しい一時的なシンボリックリンクを作成してから、既存のシンボリックリンクに名前を変更することです。
os.symlink(target, tmpLink)
os.rename(tmpLink, linkName)
それが正しく更新されたことを確認することもできます:
if os.path.realpath(linkName) == target:
# Symlink was updated
ただし、os.renameのドキュメントによると、Windowsでシンボリックリンクをアトミックに変更する方法がない場合があります。その場合は、削除して再作成するだけです。
シンボリックリンクを試み、既存のファイルが原因で失敗した場合、それを削除して再度リンクするPython2の小さな関数。最新の解決策については、 Tom Haleの回答 を確認してください。
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
os.unlink()
最初にそれを、次に os.symlink()
を使用して再作成し、新しいターゲットを指すことができます。
私は最近この質問を調査し、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)
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
で作成されたシンボリックリンクは、link_name
を置き換える前に別のプロセスによって変更される可能性があります。
pythonの問題 を発生させて、ターゲットが存在しないことを要求するos.symlink()
の問題を強調しました。 python-ideas
メーリングリストに関する私の提案
Robert Siemerの入力 へのクレジット。
E.errno!= errno.EEXISTの場合は、raiseコマンドを追加することを忘れないでください。その場合、エラーを非表示にしたくない場合:
if e.errno == errno.EEXIST:
os.remove(link_name)
os.symlink(target, link_name)
else:
raise
迅速で簡単なソリューション:
while True:
try:
os.symlink(target, link_name)
break
except FileExistsError:
os.remove(link_name)
ただし、これは、alwaysが存在するはずのシンボリックリンクを置き換えるときに、競合状態を持ちます。例:
/lib/critical.so -> /lib/critical.so.1.2
アップグレードする場合:
my_symlink('/lib/critical.so.2.0', '/lib/critical.so')
/lib/critical.so
は存在しません。
この回答 は競合状態を回避します。