80

For a given folder, how can I delete all broken links within it?

I found this answer that shows how to delete one broken link, but I can't put that together in only one line. Is there a one-liner for this?

A broken symbolic is a link that points to a file/folder that doesn't exists any longer.

Community
  • 1
  • 1
fotanus
  • 19,618
  • 13
  • 77
  • 111
  • 1
    Straight from "man find": ` find -L /usr/ports/packages -type l -exec rm -- {} + Delete all broken symbolic links in /usr/ports/packages.` – Alexander Bird Aug 29 '17 at 13:55

9 Answers9

133

Here's a POSIX way of deleting all broken symbolic links in the current directory, without recursion. It works by telling find to traverse symbolic links (-L), but stopping (-prune) at every directory-or-symbolic-link-to-such.

find -L . -name . -o -type d -prune -o -type l -exec rm {} +

You can also use a shell loop. The test -L matches symbolic links, and -e matches existing files (excluding broken symlinks).

for x in * .[!.]* ..?*; do if [ -L "$x" ] && ! [ -e "$x" ]; then rm -- "$x"; fi; done

If you want to recurse into subdirectories, this technique doesn't work. With GNU find (as found on non-embedded Linux and Cygwin), you can use the -xtype predicate to detect broken symbolic links (-xtype uses the type of the target for symbolic links, and reports l for broken links).

find -xtype l -delete

POSIXly, you need to combine two tools. You can use find -type l -exec … to invoke a command on each symbolic link, and [ -e "$x" ] to test whether that link is non-broken.

find . -type l -exec sh -c 'for x; do [ -e "$x" ] || rm "$x"; done' _ {} +

The simplest solution is to use zsh. To delete all broken symbolic links in the current directory:

rm -- *(-@D)

The characters in parentheses are glob qualifiers: - to dereference symlinks, @ to match only symlinks (the combination -@ means broken symlinks only), and D to match dot files. To recurse into subdirectories, make that:

rm -- **/*(-@D)
Selfish
  • 6,023
  • 4
  • 44
  • 63
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
30

Simple answer based on the answer you linked (for a given directory, $DIR):

find -L $DIR -maxdepth 1 -type l -delete
sanmiguel
  • 4,580
  • 1
  • 30
  • 27
  • 4
    I like this the best. Even the find man page seems to imply this is a solution for finding broken symlinks. – Felipe Alvarez Dec 28 '16 at 02:58
  • 2
    On Mac OS (BSD `find`), @sanmiguel’s command gives an error: `find: -delete: forbidden when symlinks are followed` – Diti May 18 '17 at 11:06
  • Works like a charm in CentOS – Erdal G. Nov 24 '17 at 12:21
  • What does the type -l signify here? I thought find -L was only looking at Links, but omitting the -type l flag seems to change the results. – wheeleruniverse Oct 15 '20 at 00:53
  • 1
    @JDub9 it's the difference between where to look and what to match when you look there. -L is where to look: include the contents of symlinked dirs. -type l is what to match: symlinks found when looking. – sanmiguel Oct 16 '20 at 06:07
  • 2
    @jDub9 `-L` tells `find` to return info on the file a symlink points to, or the symlink itself if the file it points to does not exist. `-type l` returns only matches that are symlinks, which immediately filters out any link targets that exist, leaving only symlinks that point to a non-existent file. – sanmiguel Mar 11 '21 at 16:27
  • Thanks @sanmiguel. That helps! – wheeleruniverse Mar 11 '21 at 16:42
10

For MAC, do a dry run as follows:-

DIR=<some path>
find -L $DIR -maxdepth 1 -type l -print

Now, you can prune the old symlinks as follows:-

for f in `find -L $DIR -maxdepth 1 -type l`; do unlink $f; done
saurabheights
  • 3,967
  • 2
  • 31
  • 50
  • Great but doesn't handle directory names with a space. – Anael Mar 31 '22 at 01:19
  • 1
    ... which is why `"$VAR"` references in the shell should habitually be put into double quotes, except you know exactly what you're doing. – Harald Jan 22 '23 at 13:04
9

From man find EXAMPLES:

find -L /usr/ports/packages -type l -exec rm -- {} +

Delete all broken symbolic links in /usr/ports/packages.

MmmHmm
  • 3,435
  • 2
  • 27
  • 49
8

The package symlinks is pre-installed on many distributions (including Ubuntu 16.04 & Fedora 25) and has some really useful features, one of which does precisely what you're looking for:

symlinks -d ./
    -d == delete dangling links
Sridhar Sarnobat
  • 25,183
  • 12
  • 93
  • 106
Sam
  • 387
  • 1
  • 6
  • 15
  • 1
    symlinks is not preinstalled on CentOS 7.3 or Ubuntu 14. Ubuntu 16 has kernel 4.10 which has this problem: https://groups.google.com/forum/#!topic/linux.kernel/C1Uk5cnrPgs. Of course, if you're not running an SSL server, then Ubuntu 16 is probably fine. – personal_cloud Sep 20 '17 at 22:25
  • Also available on Mac OS X: `sudo port install symlinks` – Sridhar Sarnobat May 10 '21 at 22:15
6

Answer based on the accepted answer to the question question "How can I find broken symlinks":

find . -type l -! -exec test -e {} \; -print | xargs rm
Community
  • 1
  • 1
rayphi
  • 503
  • 2
  • 14
  • 30
3

You can try using rmlint.

First, change directory to the folder that contains broken symlinks, and then run the following commands to find bad symlinks pointing nowhere:

rmlint --types="badlinks"

Then rmlint will create a bash script rmlint.sh in your current directory and print a list of bad symlinks in your terminal. To delete all the bad symlinks in your current directory, you can run

./rmlint.sh

Not exactly one liner, but it is very easy to use.

Erdos Yi
  • 131
  • 1
  • 3
2

You could use readlink to determine if a symlink is broken or not.

The following would list all the broken symlinks in a given directory (and subdirectories):

find . -type l -exec sh -c 'readlink -f "{}" 1>/dev/null || echo "{}"' -- "{}" \;

Replace echo with rm to get rid of the broken ones!

(I've redirected the output of readlink to /dev/null so as to avoid confusion; it'd list the target for the symlinks.)

devnull
  • 118,548
  • 33
  • 236
  • 227
  • By you nickname, I see you like redirecting to /dev/null :-) Thanks, what about `unlink`? – fotanus Feb 28 '14 at 14:49
  • @fotanus Why `unlink`, what's the problem with `rm`? – devnull Feb 28 '14 at 14:51
  • @fotanus BTW, as mentioned, in this case redirecting to `/dev/null` was purely to avoid confusion. Even if you don't, and replace `echo` with `rm`, it won't cause any sideeffect. – devnull Feb 28 '14 at 14:52
  • not sure.. because it is a link? :P I know it only removes the entry point, but isn't somewhat safer than rm? – fotanus Feb 28 '14 at 17:03
  • It seems you misunderstood what `unlink` does. It also deletes files. However `unlink`, as by POSIX, is supposed to only call the syscall `unlink` and not do anything else, while `rm` has more options and can do other things such as asking interactively confirming the delete. See [this](https://unix.stackexchange.com/questions/151951/what-is-the-difference-between-rm-and-unlink). – CherryDT Jan 15 '21 at 18:04
1

This answer is based on @sanmiguel's but with fd

fd -tl -L $DIR -X rm
number5
  • 15,913
  • 3
  • 54
  • 51