I have created symlinks to a large amount of logfiles. The syntax of the logfiles is yyyymmdd.log.gz.

To simplify things I use a simple sequence without parsing it with date:

for dd in $(seq -w 20150101 20151231) ; do 
     ln -s $origin/$dd.log.gz $target/$dd.log.gz

How do I get rid of all the broken symlinks I just created in a single fell swoop?

5 Answers 5


This simple one-liner does the job quite fast. It requires GNU Findutils:

find . -xtype l -delete

A bit of explanation:

-xtype l tests for links that are broken (it is the opposite of -type)

-delete deletes the files directly, no need for further bothering with xargs or -exec

NOTE: -xtype l means -xtype lower case L (as in link) ;)

  • 3
    Use -print -delete if you would like to display a list of what is being deleted.
    – Liam
    Mar 27, 2023 at 17:15
  • Printing the deleted links helps a lot! Thank you :) Nov 22, 2023 at 7:58
  • really the best answer. works efficiently and completely and only on broken links. without the -delete it just lists them.
    – Chris Reid
    Dec 25, 2023 at 17:05
find -L /path -type l -exec rm -i {} \;

With -L, find tries to follow symbolic links to evaluate the -type test. If it succeeds in following the link, then the link is not broken, and the -type l test will be false (as it is evaluated for the thing that the link resolves to). If it fails in following the link, then the link is broken and the -type l test will be true.

If the -type l test succeeds, then the -exec rm {} \; removes the broken link.

The suggestion to use -xtype l is a GNUism, primarily for Linux users, and won't work on non-GNU UNIX systems like Solaris, FreeBSD, etc.

  • 1
    Works on macOs. Thanks for considering alternate flavors. Jul 14, 2021 at 0:10
  • An alternative to prompt the user for confirmation is to use the -ok action instead of -exec rm -i (probably a GNU feature as well): find /path -xtype l -ok rm {} \;
    – ardnew
    Jan 28 at 4:40

With zsh (you're actually using zsh syntax in your code; with bash, you'd need to quote those variables):

rm -- $target/*(-@)


rm -- $target/<20150101-20151231>.log.gz(-@)

*(@) matches the files of type symlink. *(-@) are the ones that are still of type symlink after symlink resolution (that is, those for which the target of the symlink can't be resolved). That's equivalent to GNU find's -xtype l.

In zsh and with GNU ln, you'd rather write your loop as:

ln -srt $target -- $origin/<20150101-20151231>.log.gz

Which would also work even if $origin contains a relative path (and creates relative symlinks which reduces the risk of symlinks to be broken if some path components of the origin (the ones that are common with that of the target) are renamed in the future).


Rob Current’s answer is certainly helpful and works.

A further example can be found via man find on macOS for deleting all broken symlinks in a given directory.

Say you have a directory /usr/ports/packages containing broken symlinks to other files or directories that have either moved or been deleted, the following command will remove all symlinks for which the previously linked file location no longer exists.

    The following examples are shown as given to the shell:
    find -L /usr/ports/packages -type l -exec rm -- {} +
           Delete all broken symbolic links in /usr/ports/packages.
BSD                           September 28, 2011                           BSD

The -L switch (as per man find) causes the file information and file type returned for each symbolic link to be those of the file referenced by the link, not the link itself. If, however, the referenced file does not exist, the file information and type will be for the link itself.

The -type l switch limits the files found to those which are symbolic links.

What follows the -exec switch is the command to execute for matching files.

The -- switch causes rm, which uses getopt(3) to parse its arguments, to stop processing flag options afterwards.

As explained by man find, the syntax exec utility [argument ...] {} + has the same effect as -exec, except that ``{}'' is replaced with as many pathnames as possible for each invocation of the utility. This behaviour is similar to that of xargs(1).

Hope that’s helpful.


If all the broken links were created by an error cmd, at a given timestamp, then below works well.

ls -lrt | grep "Sep 17 15:06" | awk '{print $9}' | xargs \rm

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .