57

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?

Tibebes. M
  • 6,940
  • 5
  • 15
  • 36
zorggy
  • 571
  • 1
  • 4
  • 5

5 Answers5

76

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'
wim
  • 338,267
  • 99
  • 616
  • 750
22

You need to look at os.readlink().

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
Armin Rigo
  • 12,048
  • 37
  • 48
  • 6
    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. – SuperBiasedMan Oct 20 '15 at 15:13
  • 1
    Thank you Armin, it is exactly what I needed. I've been able to get the target and the task is now far faster. – zorggy Oct 21 '15 at 11:40
  • The original poster's problem was they did not know os.readlink() existed. This answer totally solves that problem. – gerardw Dec 23 '22 at 16:43
15

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')
SuperNova
  • 25,512
  • 7
  • 93
  • 64
  • 3
    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 :) – Tomerikoo Sep 29 '20 at 09:19
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)))
AChampion
  • 29,683
  • 4
  • 59
  • 75
  • Beware that os.path.realpath() does not currently work on Windows (http://bugs.python.org/issue9949). – antred Nov 16 '15 at 14:28
  • 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`. – Nathanael Skrepek May 18 '22 at 12:10
-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.