3:12 PM
3
A: How do I find broken symbolic links in macOS?

OrangeDogNot tested on macOS, but this should work, returning all links that cannot be followed: find -L /path/to/directory -type l -print If not. then this finds all links, and for each one runs a shell command to check whether the target exists, and if not prints the path: find /path/to/directory -typ...

 
@Gilles'SO-stopbeingevil': Did you verify "infinite loops"? When I tested, the find command seems to be detecting paths already taken and is avoiding them. In other words, I think find is automatically preventing "infinite loops".
 
I don't recall the exact command (long ago) but when it encountered circular links, instead of being infinite, it eventually stopped when it encountered the path length limit. Another command (also years ago) actually stopped and explicitly stated it had found circular links.
 
@DavidAnderson I haven't verified an infinite loop experimentally. I have verified that it lists files twices if there's a symbolic link to a sibling directory (find -L . reports both dir/somefile and link/somefile if link is a symlink to dir).
This answer is dangerously wrong: the first find command only works if there are no symbolic links to directories. find -L follows symbolic links to directories, so this command will list files outside the intended directory tree if there are symlinks to directories outside the tree and will list files more than once if there are symlinks to non-ancestor directories inside the tree.
@OrangeDog It doesn't what? I was wrong about the loop part (the native find does skip loops), but not about following outside directories, which means it could potentially traverse your entire filesystem. That is dangerous since it's traversing directories that a script shouldn't be accessing, could trigger automounts (I'm not sure about that part on macOS), etc.
 
@Gilles'SO-stopbeingevil': Ok, so it would seem if a path does not result in a loop, then the path may be traversed more than once. However, if a path would result in a loop, then the path is not traversed. BTW, I am verifying my comments using Monterey 12.5 and Snow Leopard 10.6. So, what I post I believe to be true since at least August 2009.
 
@Gilles'SO-stopbeingevil' Doesn't the -type l prevent that? The man page is unhelpfully terse but the gnu findutils docs explain that when combined with -L, it will only be true for broken symlinks. If you search with all three symlink options (-L, -H, -P) combined with the -type l primary, the other two will report the symlinks (of directories) but not any contents, while -L will report nothing.
 
3:12 PM
OrangeDog: I think it would be helpful if you could edit your answer to explain the differences between the two solutions you offer. For example: The first solution will search a directory given as the target in a symbolic link, as long as the target directory does not complete a loop. This results from the use of the -L option. The second solution has the default behavior, which is to not search a directory given as the target in a symbolic link. This is the same as when the -P option is used.
 
I should add that the man page includes find -L path -type l in its examples as the way to find broken symlinks.
 
@Mockman: Which version of macOS? I cannot find that example in any of the man find for the various versions of macOS that I am looking at.
 
@DavidAnderson I'm using Sierra but it seems to be more wide-spread than that. It is the third-last example. You could also find it on the freebsd man find page, again, as the third-last example.
 
@Mockman: The command find -L path -type l does not appear in the man find for macOS Sierra 10.12.5. The command find -L path -type l does not appear in the freebsd man find page you have referenced.
 
I substituted 'path' for '/usr/ports/packages' to make it more general.
 
3:12 PM
@Mockman: Still can not find find -L /usr/ports/packages -type l
 
@DavidAnderson Where are you looking? Here are screen shots. Hopefully this imgur link works.
 
@Mockman: With respect to the command find -L /usr/ports/packages -type l -exec rm -- {} +, I can state the following. User @Gilles'SO-stopbeingevil' has stated a broken symbolic link can be found more than once. If this happened when this command was executed, then the command would fail, because the link can only be removed once. Under the same circumstances, the command find -L /usr/ports/packages -type l would not fail. Why is it that you refused to put the entire command in you comments?
 
I don't think that was what he stated. Also, please refrain from baseless accusations.
 
3:38 PM
Can you state what the baseless accusation is?
 
 
1 hour later…
4:58 PM
@Gilles'SO-stopbeingevil' Thank you for the details on why find can be problematic on macOS and elaborating on why "not tested on macOS" needs to have more detail for some to be protected from loops and traversal outside the .
 
 
5 hours later…
10:06 PM
@bmike they are wrong though. It does not have the behaviour they describe. I said "not tested" because I wasn't sure it had the same behaviour as the GNU version w.r.t. mixing -L and -type l. Others have confirmed that it does.
 
10:39 PM
@OrangeDog: Your first command (the one with the -L option) will search a directory given as the target in a symbolic link. One consequence of this fact is a broken symbolic link will be output for each path that can be found to the broken link. Your second command (the one without the -L option) does not search a directory given as the target in a symbolic link.
Therefore, only one path can be output for each broken link. The first command can result is larger search than the second command. Under unusual conditions the entire volume may be searched. Also, the search could involve other volumes in part or in whole. In the end, both of your commands are correct.
It is up to the future users with the same question to decide which to use. The OP never specified whether the answer required use of the -L option. Although the accepted answer does not use this option. However, others who have the same question my choose use of the -L option.