web-dev-qa-db-ja.com

pythonでシンボリックリンクを変更する

Pythonであるファイルから別のファイルを指すようにシンボリックリンクを変更するにはどうすればよいですか?

os.symlink 関数は新しいシンボリックリンクを作成するためにのみ機能するようです。

33
meteoritepanama

アトミックな変更が必要な場合、リンク解除は機能しません。

より良い解決策は、新しい一時的なシンボリックリンクを作成してから、既存のシンボリックリンクに名前を変更することです。

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

それが正しく更新されたことを確認することもできます:

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

ただし、os.renameのドキュメントによると、Windowsでシンボリックリンクをアトミックに変更する方法がない場合があります。その場合は、削除して再作成するだけです。

26
lellimecnar

シンボリックリンクを試み、既存のファイルが原因で失敗した場合、それを削除して再度リンクする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
23
Robert Siemer

os.unlink() 最初にそれを、次に os.symlink() を使用して再作成し、新しいターゲットを指すことができます。

9
NPE

私は最近この質問を調査し、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)
6
culebrón

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の問題 を発生させて、ターゲットが存在しないことを要求するos.symlink()の問題を強調しました。 python-ideasメーリングリストに関する私の提案

Robert Siemerの入力 へのクレジット。

3
Tom Hale

E.errno!= errno.EEXISTの場合は、raiseコマンドを追加することを忘れないでください。その場合、エラーを非表示にしたくない場合:

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

迅速で簡単なソリューション:

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は存在しません。

この回答 は競合状態を回避します。

0
Tom Hale