Как я могу найти битые символические ссылки

282

Есть ли способ найти все символические ссылки, которые ни на что не указывают?

find ./ -type l

даст мне все символические ссылки, но не делает различий между ссылками, которые идут куда-то, и ссылками, которые этого не делают.

В настоящее время я делаю:

find ./ -type l -exec file {} \; |grep broken

Но мне интересно, какие альтернативные решения существуют.

Гейб.
источник

Ответы:

351

Я настоятельно рекомендую не использовать find -L для этой задачи (см. Объяснение ниже). Вот несколько других способов сделать это:

  • Если вы хотите использовать «чистый find» метод, он должен выглядеть так:

    find . -xtype l
    

    ( xtypeэто тест, выполненный по разыменованной ссылке) Однако, это может быть доступно не во всех версиях find. Но есть и другие варианты:

  • Вы также можете выполнить test -eизнутри findкоманду:

    find . -type l ! -exec test -e {} \; -print
    
  • Даже какой-то grepтрюк может быть лучше (т.е. безопаснее ), чем find -L, но не совсем таким, как представлено в вопросе (который включает в себя целые выходные строки, включая имена файлов):

     find . -type l -exec sh -c 'file -b "$1" | grep -q ^broken' sh {} \; -print
    

find -LТрюк цитируемый по соло из commandlinefu выглядит красиво и Hacky, но у него есть один очень опасная ловушка : Все алиасы следуют. Рассмотрим каталог с содержанием, представленным ниже:

$ ls -l
total 0
lrwxrwxrwx 1 michal users  6 May 15 08:12 link_1 -> nonexistent1
lrwxrwxrwx 1 michal users  6 May 15 08:13 link_2 -> nonexistent2
lrwxrwxrwx 1 michal users  6 May 15 08:13 link_3 -> nonexistent3
lrwxrwxrwx 1 michal users  6 May 15 08:13 link_4 -> nonexistent4
lrwxrwxrwx 1 michal users 11 May 15 08:20 link_out -> /usr/share/

Если вы запускаете find -L . -type lв этом каталоге, все также /usr/share/будет искать (и это может занять очень много времени) 1 . Для findкоманды, которая «защищена от исходящих ссылок», не используйте-L .


1 Это может выглядеть как незначительное неудобство (команда «просто» займет много времени, чтобы пройти все /usr/share), но может иметь более серьезные последствия. Например, рассмотрим среды chroot: они могут существовать в некотором подкаталоге основной файловой системы и содержать символические ссылки на абсолютные местоположения. Эти ссылки могут показаться неработающими для «внешней» системы, потому что они указывают на правильные места только после того, как вы вошли в chroot. Я также вспоминаю, что некоторые загрузчики использовали символические ссылки, /bootкоторые имели смысл только на начальной стадии загрузки, когда загрузочный раздел был смонтирован как /.

Так что, если вы используете find -Lкоманду, чтобы найти, а затем удалить битые символические ссылки из какого-то безвредного каталога, вы можете даже сломать вашу систему ...

rozcietrzewiacz
источник
11
Я думаю, что -type lэто избыточно, так как -xtype lбудет работать как -type lна не ссылки. Так find -xtype lчто, вероятно, все, что вам нужно. Спасибо за такой подход.
Quornian
3
Имейте в виду, что эти решения не работают для всех типов файловых систем. Например, он не будет работать для проверки, если /proc/XXX/exeссылка не работает. Для этого используйте test -e "$(readlink /proc/XXX/exe)".
qwertzguy
2
@Flimm find . -xtype lозначает «найти все символические ссылки, чьи (конечные) целевые файлы являются символическими ссылками». Но конечной целью символической ссылки не может быть символическая ссылка, в противном случае мы все равно можем перейти по ссылке, и она не является конечной целью. Поскольку таких символических ссылок нет, мы можем определить их как что-то еще, то есть неработающие символические ссылки.
слабый
2
@ JoóÁdám "которая может быть символической ссылкой только в случае ее разрыва". Дать "неработающей символической ссылке" или "несуществующему файлу" отдельный тип, вместо перегрузки l, меня не смущает.
Слабое
3
Предупреждение в конце полезно, но обратите внимание, что это относится не к -Lхаку, а скорее к (слепому) удалению битых символических ссылок в целом.
Алоис Махдал
38

Команда symlinksиз http://www.ibiblio.org/pub/Linux/utils/file/symlinks-1.4.tar.gz может использоваться для идентификации символических ссылок с различными характеристиками. Например:

$ rm a
$ ln -s a b
$ symlinks .
dangling: /tmp/b -> a
Сэм Моррис
источник
Этот инструмент доступен для OSX?
Qed
Неважно, получил это скомпилировано.
Qed
2
Видимо symlinksпредустановлен на Fedora.
Даниэль Йонссон
32

Как уже прокомментировал rozcietrzewiacz, это find -Lможет привести к неожиданным последствиям расширения поиска в каталоги с символическими ссылками, поэтому не является оптимальным подходом. Что еще никто не упомянул, так это то, что

find /path/to/search -xtype l

является более краткой и логически идентичной командой

find /path/to/search -type l -xtype l

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

find /path/to/search -type l -exec test ! -e {} \; -print

Для получения дополнительной информации см. Этот вопрос или ynform.org . Конечно, окончательным источником всего этого является документ findutils .

pooryorick
источник
1
Коротко, обращаемся и обращаемся к ловушке, find -Lа также к циклическим связям. +1
Flimm
Приятно. Последний работает и на MacOSX, а ответ @ rozcietrzewiacz - нет.
neu242
1
@ neu242 Это, вероятно, потому что -xtypeне указано в POSIX, и действительно, если вы посмотрите на find(1)MacOS, он имеет, -typeно не -xtype .
Прифтан
7

Я считаю, что добавление -Lфлага в вашу команду позволит вам избавиться от grep:

$ find -L . -type l

http://www.commandlinefu.com/commands/view/8260/find-broken-symlinks

от мужчины:

 -L      Cause the file information and file type (see stat(2)) returned 
         for each symbolic link to be those of the file referenced by the
         link, not the link itself. If the referenced file does not exist,
         the file information and type will be for the link itself.
kwarrick
источник
19
Сначала я проголосовал за это, но потом понял, насколько это опасно . Прежде чем использовать его, пожалуйста , посмотрите на мой ответ !
rozcietrzewiacz
6

Если вам нужно другое поведение, независимо от того, является ли ссылка разорванной или циклической, вы также можете использовать% Y с find:

$ touch a
$ ln -s a b  # link to existing target
$ ln -s c d  # link to non-existing target
$ ln -s e e  # link to itself
$ find . -type l -exec test ! -e {} \; -printf '%Y %p\n' \
   | while read type link; do
         case "$type" in
         N) echo "do something with broken link $link" ;;
         L) echo "do something with cyclic link $link" ;;
         esac
      done
do something with broken link ./d
do something with cyclic link ./e

Этот пример скопирован из этого поста (сайт удален) .

Ссылка

Энди
источник
Еще один сокращенный вариант для тех , чья findкоманда не поддерживает xtypeможет быть получена из этого: find . type l -printf "%Y %p\n" | grep -w '^N'. Когда Энди опередил меня с той же (основной) идеей в своем сценарии, я не хотел писать это как отдельный ответ. :)
синтаксическая ошибка
2

Я использую это для моего случая, и он работает довольно хорошо, так как я знаю каталог для поиска битых символических ссылок:

find -L $path -maxdepth 1 -type l

и моя папка содержит ссылку на /usr/shareнее, но не пересекает ее. Ссылки между устройствами и те, которые действительны для chroot и т. Д., Все еще являются ловушкой, но для моего случая использования этого достаточно.

Iskren
источник
2

Простой и понятный ответ, который является вариацией версии OP. Иногда вы просто хотите что-то легко напечатать или запомнить:

find . | xargs file | grep -i "broken symbolic link"
trinth
источник
1

find -L . -type l |xargs symlinks предоставит вам информацию о том, существует ли ссылка для каждого найденного файла.

Alex
источник
1

Это выведет имена неработающих символических ссылок в текущем каталоге.

for l in $(find . -type l); do cd $(dirname $l); if [ ! -e "$(readlink $(basename $l))" ]; then echo $l; fi; cd - > /dev/null; done

Работает в Баш. Не знаю о других снарядах.

conradkdotcom
источник