¿Cómo puedo encontrar enlaces simbólicos rotos?

282

¿Hay alguna manera de encontrar todos los enlaces simbólicos que no apuntan a ninguna parte?

find ./ -type l

me dará todos los enlaces simbólicos, pero no hace distinción entre enlaces que van a alguna parte y enlaces que no.

Actualmente estoy haciendo:

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

Pero me pregunto qué soluciones alternativas existen.

gabe
fuente

Respuestas:

351

Sugeriría encarecidamente no usar find -L para la tarea (ver más abajo para una explicación). Aquí hay algunas otras formas de hacer esto:

  • Si desea utilizar un findmétodo "puro ", debería verse así:

    find . -xtype l
    

    ( xtypees una prueba realizada en un enlace desreferenciado) Sin findembargo, esto puede no estar disponible en todas las versiones de . Pero también hay otras opciones:

  • También puede ejecutar test -edesde el findcomando:

    find . -type l ! -exec test -e {} \; -print
    
  • Incluso algún greptruco podría ser mejor (es decir, más seguro ) que find -L, pero no exactamente como se presenta en la pregunta (que engloba en líneas de salida completas, incluidos los nombres de archivo):

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

El find -Ltruco citado por solo de commandlinefu se ve lindo y hacky, pero tiene una trampa muy peligrosa : se siguen todos los enlaces simbólicos. Considere el directorio con los contenidos presentados a continuación:

$ 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/

Si ejecuta find -L . -type len ese directorio, /usr/share/también se buscarán todos (y eso puede llevar mucho tiempo) 1 . Para un findcomando que es "inmune a los enlaces salientes", no lo use-L .


1 Esto puede parecer un inconveniente menor (el comando "solo" tardará mucho en atravesar todo /usr/share), pero puede tener consecuencias más graves. Por ejemplo, considere entornos chroot: pueden existir en algún subdirectorio del sistema de archivos principal y contener enlaces simbólicos a ubicaciones absolutas. Es posible que esos enlaces parezcan estar rotos para el sistema "externo", ya que solo apuntan a los lugares apropiados una vez que ha ingresado al chroot. También recuerdo que algunos gestores de arranque usaron enlaces simbólicos en /bootese sentido solo en una fase de arranque inicial, cuando la partición de arranque se montó como /.

Entonces, si usa un find -Lcomando para buscar y luego eliminar enlaces simbólicos rotos de algún directorio de aspecto inofensivo, incluso podría romper su sistema ...

rozcietrzewiacz
fuente
11
Creo que -type les redundante ya -xtype lque funcionará como -type len no enlaces. Así find -xtype les, probablemente, todo lo que necesita. Gracias por este enfoque.
Quornian
3
Tenga en cuenta que esas soluciones no funcionan para todos los tipos de sistemas de archivos. Por ejemplo, no funcionará para verificar si el /proc/XXX/exeenlace está roto. Para esto, use test -e "$(readlink /proc/XXX/exe)".
qwertzguy
2
@Flimm find . -xtype lsignifica "encontrar todos los enlaces simbólicos cuyos archivos de destino (últimos) son enlaces simbólicos". Pero el objetivo final de un enlace simbólico no puede ser un enlace simbólico, de lo contrario todavía podemos seguir el enlace y no es el objetivo final. Como no existen dichos enlaces simbólicos, podemos definirlos como algo más, es decir, enlaces simbólicos rotos.
débil
2
@ JoóÁdám "que solo puede ser un enlace simbólico en caso de que esté roto". Darle a "enlace simbólico roto" o "archivo no existente" un tipo individual, en lugar de sobrecargar l, es menos confuso para mí.
débil
3
La advertencia al final es útil, pero tenga en cuenta que esto no se aplica al -Lpirateo, sino más bien a (ciegamente) eliminar enlaces simbólicos rotos en general.
Alois Mahdal
38

El symlinkscomando de http://www.ibiblio.org/pub/Linux/utils/file/symlinks-1.4.tar.gz puede usarse para identificar enlaces simbólicos con una variedad de características. Por ejemplo:

$ rm a
$ ln -s a b
$ symlinks .
dangling: /tmp/b -> a
Sam Morris
fuente
¿Esta herramienta está disponible para osx?
qed
No importa, lo tengo compilado.
qed
2
Aparentemente symlinksestá preinstalado en Fedora.
Daniel Jonsson
32

Como rozcietrzewiacz ya ha comentado, find -Lpuede tener una consecuencia inesperada de expandir la búsqueda en directorios con enlaces simbólicos, por lo que no es el enfoque óptimo. Lo que nadie ha mencionado todavía es que

find /path/to/search -xtype l

es el comando más conciso y lógicamente idéntico para

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

Ninguna de las soluciones presentadas hasta ahora detectará enlaces simbólicos cíclicos, que es otro tipo de rotura. Esta pregunta aborda la portabilidad. Para resumir, la forma portátil de encontrar enlaces simbólicos rotos, incluidos los enlaces cíclicos, es:

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

Para más detalles, vea esta pregunta o ynform.org . Por supuesto, la fuente definitiva de todo esto es la documentación de findutils .

pooryorick
fuente
1
Corto, concibe y aborda los escollos, find -Lasí como los enlaces cíclicos. +1
Flimm
Agradable. El último también funciona en MacOSX, mientras que la respuesta de @ rozcietrzewiacz no.
neu242
1
@ neu242 Eso es probablemente porque -xtypeno se ha especificado en POSIX y de hecho si nos fijamos en find(1)MacOS tiene -type, pero no -xtype .
Pryftan
7

Creo que agregar la -Lbandera a su comando le permitirá deshacerse del grep:

$ find -L . -type l

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

del hombre:

 -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
fuente
19
Al principio he votado sobre esto, pero luego me di cuenta de lo peligroso que puede ser. Antes de usarlo, por favor, eche un vistazo a mi respuesta !
rozcietrzewiacz
6

Si necesita un comportamiento diferente si el enlace está roto o cíclico, también puede usar% Y con 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

Este ejemplo se copia de esta publicación (sitio eliminado) .

Referencia

andy
fuente
Sin embargo, otra forma abreviada de aquellos cuyo findcomando no admite xtypese pueden derivar de esto: find . type l -printf "%Y %p\n" | grep -w '^N'. Como Andy me ganó con la misma idea (básica) en su guión, me resistí a escribirlo como respuesta por separado. :)
syntaxerror
2

Lo uso para mi caso y funciona bastante bien, ya que conozco el directorio para buscar enlaces simbólicos rotos:

find -L $path -maxdepth 1 -type l

y mi carpeta incluye un enlace /usr/sharepero no lo atraviesa. Los enlaces entre dispositivos y aquellos que son válidos para chroots, etc., siguen siendo un obstáculo, pero para mi caso de uso es suficiente.

Iskren
fuente
2

Respuesta simple y obvia, que es una variación de la versión de OP. A veces, solo quieres algo fácil de escribir o recordar:

find . | xargs file | grep -i "broken symbolic link"
noveno
fuente
1

find -L . -type l |xargs symlinks le proporcionará información sobre si el enlace existe o no en cada archivo encontrado.

Alex
fuente
1

Esto imprimirá los nombres de enlaces simbólicos rotos en el directorio actual.

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

Trabaja en Bash. No sé sobre otras conchas.

conradkdotcom
fuente