filesystem – How to find broken symbolic links in macOS?
1 min read

filesystem – How to find broken symbolic links in macOS?

I would like to give an answer based on grg's answer Above you will learn how to convert all found broken symlinks into a reusable shell form, portable in Bash and ZSH.

I would also like to mention the following two equivalences, they will work the same:

  • find ... ! -exec test -e {} \; That means if the pathname (which resolves symlinks) exists, it won't be found. “!” is used here by “find”. Can also replace “-not” instead of “!”
  • find ... -exec test ! -e {} \; That is, if the pathname (which resolves symlinks) doesn't exist, search for it. “!” is used here by “test”. I will use this in the example below.

Example for (1) saving, (2) printing and (3) counting the broken symlinks

pathnames=()
while IFS= read -r -d $'\0' pathname; do
  pathnames+=( "$pathname" )
done < <(find . -type l -exec test ! -e {} \; -print0)

for (( i=0; i<${#pathnames[@]}; i++ )); do
  printf "%q\n" "${pathnames[@]:"$i":1}"
done

printf "\nBroken symlinks found: %s\n" "${#pathnames[@]}"
  • pathnames is an array for storing the pathnames found, in this case broken symlinks.
  • find . -type l -exec test ! -e {} \; means checking for broken symlinks in and under the current directory. -type l finds symlinks and test ! -e tests whether the pathname resolved by the symlink does not exist.
  • -print0 means to delimit the path names found find with NUL bytes and print.
  • < <(...) means process substitution in Bash/Zsh.
  • while ... read means running read until the end of the file is reached or an error occurs.
  • IFS= means that spaces at the end/beginning of the pathname are not truncated.
  • -r means that backslashes cannot be used as escape characters; Do not interpret backslashes.
  • -d $'\0'or -d ''means that an input delimited by NUL bytes is read.
  • printf "%q\n" uses the built-in functions of bash/zsh printf to print out pathnames in a shell reusable form.
  • "${pathnames[@]:"$i":1}" Uses 0-indexed array indexing, portable array indexing in Bash/ZSH.

Further information on the while ... read Ribbon: https://mywiki.wooledge.org/BashFAQ/001

Example structure

Create some broken symlinks and broken directory symlinks with some newlines and carriage returns:

ln -s nofile 'broken symlink'
ln -s nofile $'broken symlink\nwith\nnewlines'
ln -s nofile $'broken symlink with\r carriage return'
mkdir tempdir
ln -s tempdir 'broken dir symlink'
ln -s tempdir $'broken \r dir \n_symlink'
sleep 1; rm -d tempdir
  • sleep 1 is used for Finder to catch up on displaying symlink folder icons for the two broken symlink directories.

Output to zsh and then to bash

./broken\ symlink
./broken\ symlink\ with$'\r'\ carriage\ return
./broken\ $'\r'\ dir\ $'\n'_symlink
./broken\ dir\ symlink
./broken\ symlink$'\n'with$'\n'newlines

Broken symlinks found: 5
./broken\ symlink
$'./broken symlink with\r carriage return'
$'./broken \r dir \n_symlink'
./broken\ dir\ symlink
$'./broken symlink\nwith\nnewlines'

Broken symlinks found: 5