Skip to content
shellmap

Find broken symlinks

List symlinks whose target no longer exists — useful for post-uninstall cleanup, package-manager state, and orphaned-dotfile detection.

How to find broken symlinks in each shell

Bashunix
find . -type l ! -exec test -e {} \; -print
Zshunix
find . -type l ! -exec test -e {} \; -print
Fishunix
find . -type l ! -exec test -e {} \; -print
PowerShellwindows
Get-ChildItem -Recurse -Force | Where-Object { $_.Attributes -match "ReparsePoint" -and -not (Test-Path $_.Target) }
cmd.exewindows
for /f "delims=" %f in ('dir /s /a:l /b') do @if not exist "%f" echo %f

cmd lacks a clean recursive-symlink finder. `dir /a:l` lists symlinks; `if not exist` checks if the target resolves. Junctions (NTFS dir-symlinks) are reported by `dir /a:l` too. In `.bat` file double percents: `%%f`.

Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.

Gotchas & notes

  • GNU `find` shorthand: `find . -xtype l` matches symlinks whose target type is "not present" (broken). NOT available on BSD/macOS `find` — use the portable `-type l ! -exec test -e {} \; -print` form there. The `! -exec test -e {} \;` runs `test -e` on each symlink's target; the `!` inverts to "target does NOT exist".
  • Symlinks point to STRINGS, not inodes — so `~/code/old-path` may have been valid yesterday and be broken today after a `mv`. Common sources of breakage: package uninstall (e.g. Homebrew's `cellar` symlinks broken after `brew uninstall`), git-checkout switches, `~/.config/` portability between machines, and Docker-mounted volume changes.
  • Windows junctions vs symlinks: `mklink` creates a SYMLINK (requires admin or Developer Mode since Win10 1703); `mklink /J` creates a JUNCTION (no admin needed for the same dir-target use case). `Get-Item.Attributes` flags both as `ReparsePoint`. PowerShell `Test-Path $_.Target` correctly detects broken junctions and symlinks on pwsh 6+; pwsh 5.1's `Test-Path` follows links and may return `$true` for a junction whose target was deleted but the junction record still exists.
  • After a broken-symlink audit, remove them with: bash `find . -type l ! -exec test -e {} \; -delete` (CAREFUL — verify the list first with `-print`). pwsh: `Get-ChildItem -Recurse -Force | Where-Object { $_.Attributes -match "ReparsePoint" -and -not (Test-Path $_.Target) } | Remove-Item`. For a dry-run pwsh idiom, append `-WhatIf` to `Remove-Item`.

Related commands

Related tasks