سؤال

لو اتصل os.stat() على كسر symlink, ، بيثون يلقي OSError الاستثناء.وهذا يجعلها مفيدة في العثور عليهم.ومع ذلك ، هناك عدد قليل من الأسباب الأخرى التي os.stat() قد رمي استثناء مماثل.هل هناك أدق طريقة للكشف عن كسر symlinks مع بيثون تحت لينكس ؟

هل كانت مفيدة؟

المحلول

مشترك الثعبان قوله هو أنه من الأسهل إلى طلب المغفرة من الإذن.بينما أنا لست من محبي هذا البيان في الحياة الحقيقية, أنها لا تطبق في الكثير من الحالات.عادة كنت ترغب في تجنب التعليمات البرمجية التي سلاسل اثنين نظام المكالمات على نفس الملف ، لأنك لا تعرف أبدا ما سيحدث الملف بين مكالمتين في التعليمات البرمجية الخاصة بك.

نموذجي الخطأ هو أن أكتب شيئا مثل:

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

الدعوة الثانية (نظام التشغيل.فك ارتباط) قد تفشل إذا كان هناك شيء آخر حذفه بعد إذا كان اختبار رفع استثناء ، ووقف بقية وظيفة من تنفيذ.(قد تعتقد أن هذا لا يحدث في الحياة الحقيقية, ولكن نحن فقط صيدها علة أخرى مثل ذلك من تعليمات البرمجة الأساسية الأسبوع الماضي - و كان هذا النوع من الخلل الذي غادر عدد قليل من المبرمجين الخدش رؤوسهم و يدعي '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

الانزعاج هنا هو أن القانون الأساسي يعود نفس رمز الخطأ على والرمزي أن ليس هناك كسر الارتباط الرمزي.

لذا أعتقد لا يوجد لديك خيار من كسر atomicity و تفعل شيئا مثل

if not os.path.exists(os.readlink(path)):
    print 'path %s is a broken symlink' % path

نصائح أخرى

نظام التشغيل.lstat() قد تكون مفيدة.إذا lstat() نجح ستات() فشلت ، ثم إنه ربما وصلة مكسورة.

هذا ليس الذرية لكنه يعمل.

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

في الواقع من قبل RTFM (قراءة رائعة اليدوي) ونحن نرى

نظام التشغيل.مسار.موجود(مسار)

إرجاع True إذا كان المسار يشير إلى المسار الموجود.ترجع كاذبة من أجل كسر روابط رمزية.

كما يقول:

على بعض المنابر ، هذه الوظيفة قد return False إذا إذن لا تمنح لتنفيذ نظام التشغيل.القانون الأساسي() على الملف المطلوب ، حتى إذا كان المسار جسديا موجود.

لذلك إذا كنت قلقا حول الأذونات ، يجب إضافة شروط أخرى.

يمكن أن أذكر اختبار hardlinks دون الثعبان ؟ /بن/اختبار FILE1 -ef FILE2 حالة هذا صحيح عندما الملفات مشاركة inode.

ولذلك شيئا مثل find . -type f -exec test \{} -ef /path/to/file \; -print يعمل بجد رابط الاختبار إلى ملف معين.

الذي يقودني إلى القراءة man test و يذكر -L و -h والتي على حد سواء العمل على ملف واحد و ترجع true إذا كان هذا الملف هو ارتباط رمزي, ومع ذلك لا أقول لكم إذا كان الهدف هو في عداد المفقودين.

لم أجد أن head -0 FILE1 سيعود خروج قانون 0 إذا كان الملف يمكن فتحه ، 1 إذا كان لا يمكن ، في حالة ارتباط رمزي إلى ملف منتظم يعمل اختبار ما إذا كان الهدف يمكن قراءة.

نظام التشغيل.المسار

قد حاول استخدام realpath() للحصول على ما symlink نقطة ، ثم محاولة تحديد ما إذا كان ملف صالح لاستخدام هذا الملف.

(أنا لست قادرا على أن محاولة الخروج في هذه اللحظة, لذلك سيكون لديك للعب مع حولها و انظر ما تحصل عليه)

أنا لست الثعبان الرجل لكن يبدو أن نظام التشغيل.readlink()?منطق وأود أن استخدام في بيرل هو استخدام readlink() للعثور على الهدف واستخدام القانون الأساسي() لاختبار لمعرفة ما إذا كان الهدف موجودا.

تحرير:أنا خبطت بعض بيرل أن العروض readlink.أعتقد بيرل القانون الأساسي و readlink و بايثون نظام التشغيل.القانون الأساسي() os.readlink()كلاهما wrappers نظام المكالمات ، لذلك يجب أن تترجم معقولة فضلا عن إثبات المفهوم كود:

wembley 0 /home/jj33/swap > cat p
my $f = shift;

while (my $l = readlink($f)) {
  print "$f -> $l\n";
  $f = $l;
}

if (!-e $f) {
  print "$f doesn't exist\n";
}
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 >

كان لي مشكلة مماثلة:كيف للقبض على كسر روابط الرمزية ، حتى عندما تحدث في بعض الوالدين dir?أردت أيضا أن سجل كل منهم (في تطبيق التعامل مع عدد كبير نسبيا من الملفات) ، ولكن من دون الكثير من يكرر.

هنا هو ما خطرت لي ، بما في ذلك وحدة الاختبارات.

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)

وحدة الاختبارات:

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()
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top