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 {} \; -printZshunix
find . -type l ! -exec test -e {} \; -printFishunix
find . -type l ! -exec test -e {} \; -printPowerShellwindows
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 %fcmd 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
- Find symbolic links in a directory— List every symlink under a directory tree, optionally with their targets and broken-link detection.
- List empty directories— Find directories with no files — useful for cleanup, build-output verification, and detecting failed extractions.
- Find duplicate files by content— Identify files with identical contents across a directory tree (regardless of name) — for cleanup, deduplication, or media library audits.
- List files recursively— Print every file under a directory tree, optionally as a flat list of full paths.