Найти сломанные симличины с Python -- python поле с участием linux поле с участием symlink пол Связанный проблема

Find broken symlinks with Python


25
vote

проблема

русский

Если я звоню <код> os.stat() на сломанный <код> 9988777663 , python бросает <код> OSError исключение. Это делает его полезным для их поиска. Тем не менее, есть несколько других причин, по которым <код> os.stat() может бросить аналогичное исключение. Есть ли более точный способ обнаружения сломанного <код> symlinks с python под Linux?

Английский оригинал

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

</div
        

Список ответов

30
 
vote
vote
Лучший ответ
 

Общий питон говорит, что проще просить прощения, чем разрешение. Пока я не вентилятор этого заявления в реальной жизни, он применяется во многих случаях. Обычно вы хотите избежать кода, который цепи два системных вызова в одном файле, потому что вы никогда не знаете, что будет с файлом между вашими двумя вызовами в вашем коде.

<Сильная> типичная ошибка - написать что-то вроде :

 <код> if os.path.exists(path):     os.unlink(path)   

Второй вызов (OS.unLink) может потерпеть неудачу, если что-то еще удалило его после вашего теста, поднимите исключение и остановите остаток вашей функции от выполнения. (Вы можете подумать, что это не произойдет в реальной жизни, но мы просто вытащили еще одну ошибку, подобную этому из нашей кодовой базы на прошлой неделе - и это была ошибка, которая оставила несколько программистов, почесывающих голову и утверждая «HeisenBug» для последние несколько месяцев)

Так, в вашем конкретном случае я бы, вероятно, сделаю:

 <код> try:     os.stat(path) except OSError, e:     if e.errno == errno.ENOENT:         print 'path %s does not exist or is a broken symlink' % path     else:         raise e   

раздражение вот в том, что старта возвращает тот же код ошибки для SymLink, который просто не там и разбита SymLink.

Так, я думаю, у вас нет выбора, чем сломать атомность, и сделать что-то вроде

 <код> if not os.path.exists(os.readlink(path)):     print 'path %s is a broken symlink' % path   
 

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.

A typical mistake is to write something like:

if os.path.exists(path):     os.unlink(path) 

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)

So, in your particular case, I would probably do:

try:     os.stat(path) except OSError, e:     if e.errno == errno.ENOENT:         print 'path %s does not exist or is a broken symlink' % path     else:         raise e 

The annoyance here is that stat returns the same error code for a symlink that just isn't there and a broken symlink.

So, I guess you have no choice than to break the atomicity, and do something like

if not os.path.exists(os.readlink(path)):     print 'path %s is a broken symlink' % path 
</div
 
 
       
       
12
 
vote

Это не атомное, но это работает.

<Код> os.path.islink(filename) and not os.path.exists(filename)

Действительно от RTFM (Чтение фантастического руководства) Мы видим

os.path.exists (путь)

Верните true, если путь относится к существующему пути. Возвращает false для сломанных символических ссылок.

Это также говорит:

На некоторых платформах эта функция может вернуть false, если разрешение не предоставляется выполнить os.stat () в запрошенном файле, даже если путь физически существует.

Так что, если вы беспокоитесь о разрешениях, вы должны добавить другие пункты.

 

This is not atomic but it works.

os.path.islink(filename) and not os.path.exists(filename)

Indeed by RTFM (reading the fantastic manual) we see

os.path.exists(path)

Return True if path refers to an existing path. Returns False for broken symbolic links.

It also says:

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.

So if you are worried about permissions, you should add other clauses.

</div
 
 
 
 
12
 
vote

os.lstat () может быть полезен. Если lstat () добится успеха и стата (), не удается, то это, вероятно, сломанная ссылка.

 

os.lstat() may be helpful. If lstat() succeeds and stat() fails, then it's probably a broken link.

</div
 
 
4
 
vote

Могу ли я упомянуть тестирование на жесткие линиим без Python? / bin / test имеет условие File1 -EF File2, которое верно, когда файлы имеют inode.

Следовательно, что-то вроде <Код> find . -type f -exec test {} -ef /path/to/file ; -print работает для тестирования жестких ссылок в определенный файл.

Что приводит меня к чтению <код> man test и упоминания <код> -L и <код> -h , который оба работают в одном файле и возвращает true, если этот файл Символическая ссылка, однако, что не говорит вам, если цель отсутствует.

Я нашел, что <код> head -0 FILE1 вернет выходной код <код> 0 , если файл может быть открыт и <код> 1 , если он не может, который В случае символической ссылки на обычный файл работает как тест для того, можно ли прочитать цель.

 

Can I mention testing for hardlinks without python? /bin/test has the FILE1 -ef FILE2 condition that is true when files share an inode.

Therefore, something like find . -type f -exec test {} -ef /path/to/file ; -print works for hard link testing to a specific file.

Which brings me to reading man test and the mentions of -L and -h 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.

I did find that head -0 FILE1 would return an exit code of 0 if the file can be opened and a 1 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.

</div
 
 
2
 
vote

os.path

Вы можете попробовать использовать RealPath (), чтобы получить то, к чему указывает SymLink, то пытается определить, является ли это действительный файл, использующий файл.

(Я не могу попробовать это в данный момент, поэтому вам придется играть с ним и посмотреть, что вы получаете)

 

os.path

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.

(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)

</div
 
 
2
 
vote

Я использовал этот вариант, когда SymLink сломается, она вернет ложь для Path.exists и True for Path.islink, поэтому объединение этих двух фактов мы можем использовать следующее:

 <код> def kek(argum):     if path.exists("/root/" + argum) == False and path.islink("/root/" + argum) == True:         print("The path is a broken link, location: " + os.readlink("/root/" + argum))     else:         return "No broken links fond"   
 

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:

def kek(argum):     if path.exists("/root/" + argum) == False and path.islink("/root/" + argum) == True:         print("The path is a broken link, location: " + os.readlink("/root/" + argum))     else:         return "No broken links fond" 
</div
 
 
1
 
vote

Я не парень Python, но он выглядит как OS.ReadLink ()? Логика, которую я бы использовал в Perl - использовать ReadLink (), чтобы найти цель и stat (), чтобы проверить, существует ли цель.

Редактировать: Я ударил некоторых Perl, что демонстрация ReadLink. Я считаю, что статистика Perl и ReadLink и Python's OS.Stat () и OS.Readlink () являются обоими обертанами для системных вызовов, поэтому это следует переводить разумные, как доказательство концептуального кода:

 <код> wembley 0 /home/jj33/swap > cat p my $f = shift;  while (my $l = readlink($f)) {   print "$f -> $l ";   $f = $l; }  if (!-e $f) {   print "$f doesn't exist "; } wembley 0 /home/jj33/swap > ls -l | grep ^l lrwxrwxrwx    1 jj33  users          17 Aug 21 14:30 link -> non-existant-file lrwxrwxrwx    1 root     users          31 Oct 10  2007 mm -> ../systems/mm/20071009-rewrite// lrwxrwxrwx    1 jj33  users           2 Aug 21 14:34 mmm -> mm/ wembley 0 /home/jj33/swap > perl p mm mm -> ../systems/mm/20071009-rewrite/ wembley 0 /home/jj33/swap > perl p mmm mmm -> mm mm -> ../systems/mm/20071009-rewrite/ wembley 0 /home/jj33/swap > perl p link link -> non-existant-file non-existant-file doesn't exist wembley 0 /home/jj33/swap >   
 

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.

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:

wembley 0 /home/jj33/swap > cat p my $f = shift;  while (my $l = readlink($f)) {   print "$f -> $l ";   $f = $l; }  if (!-e $f) {   print "$f doesn't exist "; } wembley 0 /home/jj33/swap > ls -l | grep ^l lrwxrwxrwx    1 jj33  users          17 Aug 21 14:30 link -> non-existant-file lrwxrwxrwx    1 root     users          31 Oct 10  2007 mm -> ../systems/mm/20071009-rewrite// lrwxrwxrwx    1 jj33  users           2 Aug 21 14:34 mmm -> mm/ wembley 0 /home/jj33/swap > perl p mm mm -> ../systems/mm/20071009-rewrite/ wembley 0 /home/jj33/swap > perl p mmm mmm -> mm mm -> ../systems/mm/20071009-rewrite/ wembley 0 /home/jj33/swap > perl p link link -> non-existant-file non-existant-file doesn't exist wembley 0 /home/jj33/swap > 
</div
 
 
0
 
vote

У меня была похожая проблема: как поймать сломанные симличины, даже когда они возникают в каком-либо родительском режиме? Я также хотел войти все из них (в приложении с довольно большим количеством файлов), но без слишком много повторов.

Вот то, с чем я придумал, включая модульные тесты.

<Сильные> Fileutil.py :

 <код> import os from functools import lru_cache import logging  logger = logging.getLogger(__name__)  @lru_cache(maxsize=2000) def check_broken_link(filename):     """     Check for broken symlinks, either at the file level, or in the     hierarchy of parent dirs.     If it finds a broken link, an ERROR message is logged.     The function is cached, so that the same error messages are not repeated.      Args:         filename: file to check      Returns:         True if the file (or one of its parents) is a broken symlink.         False otherwise (i.e. either it exists or not, but no element         on its path is a broken link).      """     if os.path.isfile(filename) or os.path.isdir(filename):         return False     if os.path.islink(filename):         # there is a symlink, but it is dead (pointing nowhere)         link = os.readlink(filename)         logger.error('broken symlink: {} -> {}'.format(filename, link))         return True     # ok, we have either:     #   1. a filename that simply doesn't exist (but the containing dir            does exist), or     #   2. a broken link in some parent dir     parent = os.path.dirname(filename)     if parent == filename:         # reached root         return False     return check_broken_link(parent)   

Агрегатные тесты:

 <код> man test0  
 

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.

Here is what I came up with, including unit tests.

fileutil.py:

import os from functools import lru_cache import logging  logger = logging.getLogger(__name__)  @lru_cache(maxsize=2000) def check_broken_link(filename):     """     Check for broken symlinks, either at the file level, or in the     hierarchy of parent dirs.     If it finds a broken link, an ERROR message is logged.     The function is cached, so that the same error messages are not repeated.      Args:         filename: file to check      Returns:         True if the file (or one of its parents) is a broken symlink.         False otherwise (i.e. either it exists or not, but no element         on its path is a broken link).      """     if os.path.isfile(filename) or os.path.isdir(filename):         return False     if os.path.islink(filename):         # there is a symlink, but it is dead (pointing nowhere)         link = os.readlink(filename)         logger.error('broken symlink: {} -> {}'.format(filename, link))         return True     # ok, we have either:     #   1. a filename that simply doesn't exist (but the containing dir            does exist), or     #   2. a broken link in some parent dir     parent = os.path.dirname(filename)     if parent == filename:         # reached root         return False     return check_broken_link(parent) 

Unit tests:

import logging import shutil import tempfile import os  import unittest from ..util import fileutil   class TestFile(unittest.TestCase):      def _mkdir(self, path, create=True):         d = os.path.join(self.test_dir, path)         if create:             os.makedirs(d, exist_ok=True)         return d      def _mkfile(self, path, create=True):         f = os.path.join(self.test_dir, path)         if create:             d = os.path.dirname(f)             os.makedirs(d, exist_ok=True)             with open(f, mode='w') as fp:                 fp.write('hello')         return f      def _mklink(self, target, path):         f = os.path.join(self.test_dir, path)         d = os.path.dirname(f)         os.makedirs(d, exist_ok=True)         os.symlink(target, f)         return f      def setUp(self):         # reset the lru_cache of check_broken_link         fileutil.check_broken_link.cache_clear()          # create a temporary directory for our tests         self.test_dir = tempfile.mkdtemp()          # create a small tree of dirs, files, and symlinks         self._mkfile('a/b/c/foo.txt')         self._mklink('b', 'a/x')         self._mklink('b/c/foo.txt', 'a/f')         self._mklink('../..', 'a/b/c/y')         self._mklink('not_exist.txt', 'a/b/c/bad_link.txt')         bad_path = self._mkfile('a/XXX/c/foo.txt', create=False)         self._mklink(bad_path, 'a/b/c/bad_path.txt')         self._mklink('not_a_dir', 'a/bad_dir')      def tearDown(self):         # Remove the directory after the test         shutil.rmtree(self.test_dir)      def catch_check_broken_link(self, expected_errors, expected_result, path):         filename = self._mkfile(path, create=False)         with self.assertLogs(level='ERROR') as cm:             result = fileutil.check_broken_link(filename)             logging.critical('nothing')  # trick: emit one extra message, so the with assertLogs block doesn't fail         error_logs = [r for r in cm.records if r.levelname is 'ERROR']         actual_errors = len(error_logs)         self.assertEqual(expected_result, result, msg=path)         self.assertEqual(expected_errors, actual_errors, msg=path)      def test_check_broken_link_exists(self):         self.catch_check_broken_link(0, False, 'a/b/c/foo.txt')         self.catch_check_broken_link(0, False, 'a/x/c/foo.txt')         self.catch_check_broken_link(0, False, 'a/f')         self.catch_check_broken_link(0, False, 'a/b/c/y/b/c/y/b/c/foo.txt')      def test_check_broken_link_notfound(self):         self.catch_check_broken_link(0, False, 'a/b/c/not_found.txt')      def test_check_broken_link_badlink(self):         self.catch_check_broken_link(1, True, 'a/b/c/bad_link.txt')         self.catch_check_broken_link(0, True, 'a/b/c/bad_link.txt')      def test_check_broken_link_badpath(self):         self.catch_check_broken_link(1, True, 'a/b/c/bad_path.txt')         self.catch_check_broken_link(0, True, 'a/b/c/bad_path.txt')      def test_check_broken_link_badparent(self):         self.catch_check_broken_link(1, True, 'a/bad_dir/c/foo.txt')         self.catch_check_broken_link(0, True, 'a/bad_dir/c/foo.txt')         # bad link, but shouldn't log a new error:         self.catch_check_broken_link(0, True, 'a/bad_dir/c')         # bad link, but shouldn't log a new error:         self.catch_check_broken_link(0, True, 'a/bad_dir')  if __name__ == '__main__':     unittest.main() 
</div
 
 

Связанный проблема

0  Jupyter-Notebook не работает после команды Jupyter-Notebook  ( Jupyter notebook is not working after jupyter notebook command ) 
Я устанавливаю Jupyter-ноутбук для работы с проектами, связанными с наукой на науке. Я не могу установить его должным образом или есть некоторая проблема с со...

2  Spotipy Authentication не возвращает токен?  ( Spotipy authentication not returning token ) 
Все подходящие люди, я прочитал всю документацию, которую я могу найти в методах аутентификации Spotipy, и я собрал небольшой кусок кода, чтобы проверить его,...

1  Используйте файл в качестве аргумента для функции, которая подсчитывает, создает счет для каждой буквы в текстовом файле  ( Use a file as an argument for a function that counts creates a count for each le ) 
Моя программа прямо сейчас работает только для первого символа в текстовом файле и итерации бесконечно. Например, если первая буква в текстовом файле D, прогр...

0  Групповые пакеты на сервере трафика  ( Timing packets on a traffic server ) 
У меня есть прокси-сервер трафика, который является дополнительным ходом на сеть и обрабатывает большое количество трафика. Я хотел бы рассчитать стоимость ...

2  Pyproj Пакет Установка Proj_dir Переменная  ( Pyproj package installation proj dir variable ) 
Я пытаюсь установить <код> pyproj , хотя получаю <код> Proj executable not found. Please set PROJ_DIR variable ошибки. Команда <Код> $ pipenv install pyp...

0  Почему мой скрипт Cron работает только один раз?  ( Why my cron script running just once ) 
Я пытаюсь запустить простой сценарий Python, используя команды cron. Я написал код, чтобы запускать свой скрипт Python в каждом 1 минуте и записывать вывод в ...

0  Как добавить номер в каждом базовом случае рекурсии? (Python3)?  ( How to add number in each base case of recursionpython3 ) 
Я хочу добавить 1 на <код> ans После того, как рекурсионная ветка достигает того, где <код> n==0 но не удается. Я пытался присвоить <код> ans сначала и д...

48  Ошибка auth_user с django 1.8 и syncdb / migrate  ( Auth user error with django 1 8 and syncdb migrate ) 
При обновлении в Django 1.8 (с zc.buildout) и запуском SyncDB или миграция, я получаю это сообщение: <Код> django.db.utils.ProgrammingError: relation "auth_...

0  Ubuntu Supervisor Импорт модулей Python модулей  ( Ubuntu supervisor import python modules error ) 
Я использую супервизор для запуска Websocket Django в системе запуска. Когда я запускаю руководитель, он будет поднять ModulenotfoundError: Нет модуля с ...

0  Использование Pybind11 на MacBook Pro  ( Using pybind11 on macbook pro ) 
Я пытаюсь использовать Pybind11 на MacBook, и я пытаюсь сделать так, как было написано в https://pybind11.readtheDocs.io/en/stable/basics.html я установил C...

3  Compile United с кодом ошибки 1 в / tmp / pip_build_root / uwsgi  ( Compile failed with error code 1 in tmp pip build root uwsgi ) 
Я пытаюсь следить за руководством в http: // uwsgi -docs.readtheDocks.org/en/latest/tutorials/django_and_nginx.html . Я получил все, что все работает TOHTTP:...

1  Python: Tkinter не отображает мой образ или интерфейс  ( Python tkinter not displaying my image or ui ) 
Я пытаюсь создать пользовательский интерфейс с изображением в правом верхнем углу. Вот мой код: <код> import tkinter as tk import urllib.request import base...

1  dataframe или sqlctx (sqlcontext) сгенерировали "попытка вызвать пакет" ошибка  ( Dataframe or sqlctx sqlcontext generated trying to call a package error ) 
Я использую Spark 1.3.1. В Pyspark я создал Dataframe от RDD и зарегистрировал схему, что-то вроде этого: <код> dataLen=sqlCtx.createDataFrame(myrdd, ["id",...

15  Пип не работает  ( Pip not working ) 
Я пытаюсь установить Python-Shavely с Pip в Ubuntu 10.04. Я получил «неизвестную или неподдерживаемую команду» «Установить», пока я попробовал, <Код> Allian...

12  Редактировать с помощью контекстного меню IDLE (Python GUI) в Windows & NBSP; 7  ( Edit with idle python gui context menu on windowsnbsp7 ) 
Под Windows XP у меня есть контекстное введение меню «Редактировать с простым» для python исходные файлы. Я пытался сделать эту работу под Windows 7 (32-бит...