Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Is there a way to find all symbolic links that don't point anywere?

find ./ -type l

will give me all symbolic links, but makes no distinction between links that go somewhere and links that don't.

I'm currently doing:

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

But I'm wondering what alternate solutions exist.

share|improve this question

6 Answers 6

up vote 73 down vote accepted

I'd strongly suggest not to use find -L for the task (see below for explanation). Here are some other ways to do this:

  • If you want to use a "pure find" method, it should rather look like this:

    find . -type l -xtype l
    

    (xtype is a test performed on a dereferenced link) This may not be available in all versions of find, though. But there are other options as well;

  • You can also exec test -e from within the find command:

    find . -type l -! -exec test -e {} \; -print
    
  • Even some grep trick could be better (i.e. safer) than find -L, but not exactly such as presented in the question (which greps in entire output lines, including filenames):

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

The find -L trick quoted by solo from commandlinefu looks nice and hacky, but it has one very dangerous pitfall: All the symlinks are followed. Consider directory with the contents presented below:

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

If you run find -L . -type l in that directory, all /usr/share/ would be searched as well (and that can take really long)1. For a find command that is "immune to outgoing links", don't use -L.


1 This may look like a minor inconvenience (the command will "just" take long to traverse all /usr/share) - but can have more severe consequences. For instance, consider chroot environments: They can exist in some subdirectory of the main filesystem and contain symlinks to absolute locations. Those links could seem to be broken for the "outside" system, because they only point to proper places once you've entered the chroot. I also recall that some bootloader used symlinks under /boot that only made sense in an initial boot phase, when the boot partition was mounted as /.

So if you use a find -L command to find and then delete broken symlinks from some harmless-looking directory, you might even break your system...

share|improve this answer
    
This is something I hadn't considered @rozcietrzewiacz and is something that will definitely effect my particular case. Thanks for the thorough follow-up. –  gabe. May 15 '12 at 15:46
    
I think this might be the answer to a major performance problem we've been having with a particular script. Thanks! –  Kenny Rasschaert Jun 11 '12 at 9:37
    
This is awesome! I've been writing throw-away Python script for this particular one!! :-/ –  Lester Cheung Oct 19 '12 at 0:02
3  
I think -type l is redundant since -xtype l will operate as -type l on non-links. So find -xtype l is probably all you need. Thanks for this approach. –  quornian Nov 17 '12 at 21:56
    
@quornian Great catch! Indeed, find -xtype l is enough. The only difference is in the number of system calls performed by each command (which indicates find -type l -xtype l should be faster). But I guess this would make a difference only on a large filesystem trees. –  rozcietrzewiacz Nov 20 '12 at 20:08

find -L . -type l |xargs symlinks will give you info whether the link exists or not on a per foundfile basis.

share|improve this answer

If you need a different behavior whether the link is broken or cyclic you can also use %Y with 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

This example is copied from this post.

share|improve this answer
    
Thank you @andy –  gabe. Dec 27 '12 at 21:26

As rozcietrzewiacz has already commented, find -L can have unexpected consequence of expanding the search into symlinked directories, so isn't the optimal approach. What no one has mentioned yet is that

find /path/to/search -xtype l

is the more concise, and logically identical command to

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

None of the solutions presented so far will detect cyclic symlinks, which is another type of breakage. this question addresses portability. To summarize, the portable way to find broken symbolic links, including cyclic links, is:

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

For more details, see this question or ynform.org. Of course, the definitive source for all this is the findutils documentaton.

share|improve this answer
    
Short, consice, and addresses the find -L pitfall as well as cyclical links. +1 –  Flimm Oct 7 at 13:00

I believe adding the -L flag to your command will allow you do get rid of the grep:

$ find -L . -type l

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

from the man:

 -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.
share|improve this answer
    
Thanks, not sure how I missed that in the man page! –  gabe. Mar 15 '12 at 17:40
8  
At first I've upvoted this, but then I've realised how dangerous it may be. Before you use it, please have a look at my answer! –  rozcietrzewiacz May 15 '12 at 7:37

The symlinks command from http://www.ibiblio.org/pub/Linux/utils/file/symlinks-1.4.tar.gz can be used to identify symlinks with a variety of characteristics. For instance:

$ rm a
$ ln -s a b
$ symlinks .
dangling: /tmp/b -> a
share|improve this answer
    
Is this tool available for osx? –  qed Jul 27 at 20:32
    
Never mind, got it compiled. –  qed Jul 27 at 20:51

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.