62

Using Python, I need to check whether hundreds of symlinks are correct and recreate them when not. What I do now is to compare real paths of what I want and what I have, but it's slow because it's over NFS with an automount.

Otherwise I'm going to run a subprocess with the command 'ls -l' and work on the list of strings returned. I would prefer a better solution, using a Python library...

Edit1: I have: link_name -> link_target and then link_target -> a_real_file. What I need is to extract link_target from link_name, not a_real_file. I don't care if the real file does not exist.

Edit2: Maybe I did not express correctly. What I mean by a correct symlink is 'a link that point to a predefined path, even if it does not exist'. So I need to check that:

link_name_1 -> target_1
link_name_2 -> target_2

That's why I need to extract targets, not the real paths. Then I compare them to a reference (dictionary). So my question is: How do I extract the target path?

5 Answers 5

80

The problem with os.readlink() is it will only resolve 1 step of the link. We can have a situation where A links to another link B, and B link is dangling.

$ ln -s /tmp/example/notexist /tmp/example/B
$ ln -s /tmp/example/B /tmp/example/A
$ ls -l /tmp/example
A -> /tmp/example/B
B -> /tmp/example/notexist

Now in Python, os.readlink gives you the first target.

>>> import os
>>> os.readlink('A')
'/tmp/example/B'

But in most situations I assume we are interested in the resolved path. So pathlib can help here:

>>> from pathlib import Path
>>> Path('A').resolve()
PosixPath('/tmp/example/notexist')

For older Python versions:

>>> os.path.realpath('A')
'/tmp/example/notexist'
Sign up to request clarification or add additional context in comments.

2 Comments

let me tell you from my own experience that in Python 2.7 on Windows (at least in my environment -- PY 2.7.15 32bit, W 11 Pro 24H2 26100.2894), python 2.7 returns c:\.....\file.txt while python 3.10 returns E:\.....\file.txt for the same file which is inside a directory junction pointing to E: located on c:.
You can use a combination of islink and readlink for the resolved path in cases of dangling links if you want to stick to os. path = 'A'; while os.path.islink(path): path = os.path.readlink(path)
23

You need to look at os.readlink().

3 Comments

Please consider editing your post to add more explanation about what your code does and why it will solve the problem. An answer that mostly just contains code (even if it's working) usually wont help the OP to understand their problem.
Thank you Armin, it is exactly what I needed. I've been able to get the target and the task is now far faster.
The original poster's problem was they did not know os.readlink() existed. This answer totally solves that problem.
19

In Python 3.9 or above, pathlib.Path.readlink() can be used.

>>> p = Path('mylink')
>>> p.symlink_to('setup.py')
>>> p.readlink()
PosixPath('setup.py')

1 Comment

I think everyone should stop using os.path and move to pathlib where possible. Nice work updating old questions with new features. +1 for hopefully this will reach upwards :)
3

To determine if a link is broken, you can, os.walk and test os.path.exists(path) which will return False for a broken link. You can then use os.path.realpath(path) to find out what the link is supposed to be pointing to.
Something like (untested):

for root, dirs, files in os.walk('<path>'):
    for file in files:
         f = os.path.join(root, file)
         if os.path.islink(f) and not os.path.exists(f):
             print("Broken: {} -> {}".format(f, os.path.realpath(f)))

2 Comments

Beware that os.path.realpath() does not currently work on Windows (bugs.python.org/issue9949).
For some reason I cannot edit this post. So I just write my edits as a comment: the method os.join in the 3rd line does not exists it has to be os.path.join.
-3

To determine if a directory entry is a symlink use this:

os.path.islink(path)

Return True if path refers to a directory entry that is a symbolic link. Always False if symbolic links are not supported.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.