<p>If I call <code>os.stat()</code> on a broken <code>symlink</code>, python throws an <code>OSError</code> exception. This makes it useful for finding them. However, there are a few other reasons that <code>os.stat()</code> might throw a similar exception. Is there a more precise way of detecting broken <code>symlinks</code> with Python under Linux?</p>

[{'answer_id': 20843, 'author': 'dlamblin', 'author_id': 459, 'author_profile': 'https://Stackoverflow.com/users/459', 'pm_score': 2, 'selected': False, 'text': "<p>Can I mention testing for hardlinks without python? /bin/test has the FILE1 -ef FILE2 condition that is true when files share an inode.</p>\n\n<p>Therefore, something like <code>find . -type f -exec test \\{} -ef /path/to/file \\; -print</code> works for hard link testing to a specific file.</p>\n\n<p>Which brings me to reading <code>man test</code> and the mentions of <code>-L</code> and <code>-h</code> which both work on one file and return true if that file is a symbolic link, however that doesn't tell you if the target is missing.</p>\n\n<p>I did find that <code>head -0 FILE1</code> would return an exit code of <code>0</code> if the file can be opened and a <code>1</code> if it cannot, which in the case of a symbolic link to a regular file works as a test for whether it's target can be read.</p>\n"}, {'answer_id': 20845, 'author': 'jj33', 'author_id': 430, 'author_profile': 'https://Stackoverflow.com/users/430', 'pm_score': 1, 'selected': False, 'text': '<p>I\'m not a python guy but it looks like os.readlink()?  The logic I would use in perl is to use readlink() to find the target and the use stat() to test to see if the target exists.</p>\n\n<p>Edit: I banged out some perl that demos readlink.  I believe perl\'s stat and readlink and python\'s os.stat() and os.readlink()are both wrappers for the system calls, so this should translate reasonable well as proof of concept code:</p>\n\n<pre><code>wembley 0 /home/jj33/swap &gt; cat p\nmy $f = shift;\n\nwhile (my $l = readlink($f)) {\n  print "$f -&gt; $l\\n";\n  $f = $l;\n}\n\nif (!-e $f) {\n  print "$f doesn\'t exist\\n";\n}\nwembley 0 /home/jj33/swap &gt; ls -l | grep ^l\nlrwxrwxrwx    1 jj33  users          17 Aug 21 14:30 link -&gt; non-existant-file\nlrwxrwxrwx    1 root     users          31 Oct 10  2007 mm -&gt; ../systems/mm/20071009-rewrite//\nlrwxrwxrwx    1 jj33  users           2 Aug 21 14:34 mmm -&gt; mm/\nwembley 0 /home/jj33/swap &gt; perl p mm\nmm -&gt; ../systems/mm/20071009-rewrite/\nwembley 0 /home/jj33/swap &gt; perl p mmm\nmmm -&gt; mm\nmm -&gt; ../systems/mm/20071009-rewrite/\nwembley 0 /home/jj33/swap &gt; perl p link\nlink -&gt; non-existant-file\nnon-existant-file doesn\'t exist\nwembley 0 /home/jj33/swap &gt;\n</code></pre>\n'}, {'answer_id': 20848, 'author': 'Greg Hewgill', 'author_id': 893, 'author_profile': 'https://Stackoverflow.com/users/893', 'pm_score': 4, 'selected': False, 'text': '<p><a href="https://docs.python.org/2/library/os.html#os.lstat" rel="nofollow noreferrer">os.lstat()</a> may be helpful. If lstat() succeeds and stat() fails, then it\'s probably a broken link.</p>\n'}, {'answer_id': 20859, 'author': 'Jason Baker', 'author_id': 2147, 'author_profile': 'https://Stackoverflow.com/users/2147', 'pm_score': 2, 'selected': False, 'text': '<p><a href="http://docs.python.org/lib/module-os.path.html" rel="nofollow noreferrer">os.path</a></p>\n\n<p>You may try using realpath() to get what the symlink points to, then trying to determine if it\'s a valid file using is file.</p>\n\n<p>(I\'m not able to try that out at the moment, so you\'ll have to play around with it and see what you get)</p>\n'}, {'answer_id': 26957, 'author': 'Thomas Vander Stichele', 'author_id': 2900, 'author_profile': 'https://Stackoverflow.com/users/2900', 'pm_score': 6, 'selected': True, 'text': "<p>A common Python saying is that it's easier to ask forgiveness than permission.  While I'm not a fan of this statement in real life, it does apply in a lot of cases.  Usually you want to avoid code that chains two system calls on the same file, because you never know what will happen to the file in between your two calls in your code.</p>\n\n<p><strong>A typical mistake is to write something like</strong>:</p>\n\n<pre><code>if os.path.exists(path):\n    os.unlink(path)\n</code></pre>\n\n<p>The second call (os.unlink) may fail if something else deleted it after your if test, raise an Exception, and stop the rest of your function from executing.  (You might think this doesn't happen in real life, but we just fished another bug like that out of our codebase last week - and it was the kind of bug that left a few programmers scratching their head and claiming 'Heisenbug' for the last few months)</p>\n\n<p>So, in your particular case, I would probably do:</p>\n\n<pre><code>try:\n    os.stat(path)\nexcept OSError, e:\n    if e.errno == errno.ENOENT:\n        print 'path %s does not exist or is a broken symlink' % path\n    else:\n        raise e\n</code></pre>\n\n<p>The annoyance here is that stat returns the same error code for a symlink that just isn't there and a broken symlink.</p>\n\n<p>So, I guess you have no choice than to break the atomicity, and do something like</p>\n\n<pre><code>if not os.path.exists(os.readlink(path)):\n    print 'path %s is a broken symlink' % path\n</code></pre>\n"}, {'answer_id': 31102280, 'author': 'am70', 'author_id': 5058564, 'author_profile': 'https://Stackoverflow.com/users/5058564', 'pm_score': 4, 'selected': False, 'text': '<p>This is not atomic but it works.</p>\n<p><code> os.path.islink(filename) and not os.path.exists(filename)</code></p>\n<p>Indeed by <a href="https://docs.python.org/2/library/os.path.html" rel="noreferrer">RTFM</a>\n(reading the fantastic manual) we see</p>\n<blockquote>\n<p>os.path.exists(path)</p>\n<p>Return True if path refers to an existing path. Returns False for  broken symbolic links.</p>\n</blockquote>\n<p>It also says:</p>\n<blockquote>\n<p>On some platforms, this function may return False if permission is not granted to execute os.stat() on the requested file, even if the path physically exists.</p>\n</blockquote>\n<p>So if you are worried about permissions, you should add other clauses.</p>\n'}, {'answer_id': 40274852, 'author': 'Pierre D', 'author_id': 758174, 'author_profile': 'https://Stackoverflow.com/users/758174', 'pm_score': 0, 'selected': False, 'text': '<p>I had a similar problem: how to catch broken symlinks, even when they occur in some parent dir?  I also wanted to log all of them (in an application dealing with a fairly large number of files), but without too many repeats.</p>\n\n<p>Here is what I came up with, including unit tests.</p>\n\n<p><strong>fileutil.py</strong>:</p>\n\n<pre><code>import os\nfrom functools import lru_cache\nimport logging\n\nlogger = logging.getLogger(__name__)\n\n@lru_cache(maxsize=2000)\ndef check_broken_link(filename):\n    """\n    Check for broken symlinks, either at the file level, or in the\n    hierarchy of parent dirs.\n    If it finds a broken link, an ERROR message is logged.\n    The function is cached, so that the same error messages are not repeated.\n\n    Args:\n        filename: file to check\n\n    Returns:\n        True if the file (or one of its parents) is a broken symlink.\n        False otherwise (i.e. either it exists or not, but no element\n        on its path is a broken link).\n\n    """\n    if os.path.isfile(filename) or os.path.isdir(filename):\n        return False\n    if os.path.islink(filename):\n        # there is a symlink, but it is dead (pointing nowhere)\n        link = os.readlink(filename)\n        logger.error(\'broken symlink: {} -&gt; {}\'.format(filename, link))\n        return True\n    # ok, we have either:\n    #   1. a filename that simply doesn\'t exist (but the containing dir\n           does exist), or\n    #   2. a broken link in some parent dir\n    parent = os.path.dirname(filename)\n    if parent == filename:\n        # reached root\n        return False\n    return check_broken_link(parent)\n</code></pre>\n\n<p>Unit tests:</p>\n\n<pre><code>import logging\nimport shutil\nimport tempfile\nimport os\n\nimport unittest\nfrom ..util import fileutil\n\n\nclass TestFile(unittest.TestCase):\n\n    def _mkdir(self, path, create=True):\n        d = os.path.join(self.test_dir, path)\n        if create:\n            os.makedirs(d, exist_ok=True)\n        return d\n\n    def _mkfile(self, path, create=True):\n        f = os.path.join(self.test_dir, path)\n        if create:\n            d = os.path.dirname(f)\n            os.makedirs(d, exist_ok=True)\n            with open(f, mode=\'w\') as fp:\n                fp.write(\'hello\')\n        return f\n\n    def _mklink(self, target, path):\n        f = os.path.join(self.test_dir, path)\n        d = os.path.dirname(f)\n        os.makedirs(d, exist_ok=True)\n        os.symlink(target, f)\n        return f\n\n    def setUp(self):\n        # reset the lru_cache of check_broken_link\n        fileutil.check_broken_link.cache_clear()\n\n        # create a temporary directory for our tests\n        self.test_dir = tempfile.mkdtemp()\n\n        # create a small tree of dirs, files, and symlinks\n        self._mkfile(\'a/b/c/foo.txt\')\n        self._mklink(\'b\', \'a/x\')\n        self._mklink(\'b/c/foo.txt\', \'a/f\')\n        self._mklink(\'../..\', \'a/b/c/y\')\n        self._mklink(\'not_exist.txt\', \'a/b/c/bad_link.txt\')\n        bad_path = self._mkfile(\'a/XXX/c/foo.txt\', create=False)\n        self._mklink(bad_path, \'a/b/c/bad_path.txt\')\n        self._mklink(\'not_a_dir\', \'a/bad_dir\')\n\n    def tearDown(self):\n        # Remove the directory after the test\n        shutil.rmtree(self.test_dir)\n\n    def catch_check_broken_link(self, expected_errors, expected_result, path):\n        filename = self._mkfile(path, create=False)\n        with self.assertLogs(level=\'ERROR\') as cm:\n            result = fileutil.check_broken_link(filename)\n            logging.critical(\'nothing\')  # trick: emit one extra message, so the with assertLogs block doesn\'t fail\n        error_logs = [r for r in cm.records if r.levelname is \'ERROR\']\n        actual_errors = len(error_logs)\n        self.assertEqual(expected_result, result, msg=path)\n        self.assertEqual(expected_errors, actual_errors, msg=path)\n\n    def test_check_broken_link_exists(self):\n        self.catch_check_broken_link(0, False, \'a/b/c/foo.txt\')\n        self.catch_check_broken_link(0, False, \'a/x/c/foo.txt\')\n        self.catch_check_broken_link(0, False, \'a/f\')\n        self.catch_check_broken_link(0, False, \'a/b/c/y/b/c/y/b/c/foo.txt\')\n\n    def test_check_broken_link_notfound(self):\n        self.catch_check_broken_link(0, False, \'a/b/c/not_found.txt\')\n\n    def test_check_broken_link_badlink(self):\n        self.catch_check_broken_link(1, True, \'a/b/c/bad_link.txt\')\n        self.catch_check_broken_link(0, True, \'a/b/c/bad_link.txt\')\n\n    def test_check_broken_link_badpath(self):\n        self.catch_check_broken_link(1, True, \'a/b/c/bad_path.txt\')\n        self.catch_check_broken_link(0, True, \'a/b/c/bad_path.txt\')\n\n    def test_check_broken_link_badparent(self):\n        self.catch_check_broken_link(1, True, \'a/bad_dir/c/foo.txt\')\n        self.catch_check_broken_link(0, True, \'a/bad_dir/c/foo.txt\')\n        # bad link, but shouldn\'t log a new error:\n        self.catch_check_broken_link(0, True, \'a/bad_dir/c\')\n        # bad link, but shouldn\'t log a new error:\n        self.catch_check_broken_link(0, True, \'a/bad_dir\')\n\nif __name__ == \'__main__\':\n    unittest.main()\n</code></pre>\n'}, {'answer_id': 64213004, 'author': 'Владислав Шибалов', 'author_id': 14395948, 'author_profile': 'https://Stackoverflow.com/users/14395948', 'pm_score': 2, 'selected': False, 'text': '<p>I used this variant, When symlink is broken it will return false for the path.exists and true for path.islink, so combining this two facts we may use the following:</p>\n<pre><code>def kek(argum):\n    if path.exists(&quot;/root/&quot; + argum) == False and path.islink(&quot;/root/&quot; + argum) == True:\n        print(&quot;The path is a broken link, location: &quot; + os.readlink(&quot;/root/&quot; + argum))\n    else:\n        return &quot;No broken links fond&quot;\n</code></pre>\n'}]
['https://Stackoverflow.com/questions/20794', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/1892/']