How to get symlink target in Python?
Asked Answered
W

5

60

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?

Whitnell answered 20/10, 2015 at 9:5 Comment(0)
B
78

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'
Bayer answered 23/2, 2017 at 21:53 Comment(0)
P
23

You need to look at os.readlink().

Plate answered 20/10, 2015 at 11:41 Comment(3)
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.Isaak
Thank you Armin, it is exactly what I needed. I've been able to get the target and the task is now far faster.Whitnell
The original poster's problem was they did not know os.readlink() existed. This answer totally solves that problem.Impressure
E
17

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')
Enchanting answered 29/9, 2020 at 8:25 Comment(1)
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 :)Our
C
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)))
Contradict answered 20/10, 2015 at 9:18 Comment(2)
Beware that os.path.realpath() does not currently work on Windows (bugs.python.org/issue9949).Crest
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.Installment
I
-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.

Isotropic answered 20/10, 2015 at 9:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.