How can I detect whether a symlink is broken in Bash?
Asked Answered
C

8

65

I run find and iterate through the results with [ \( -L $F \) ] to collect certain symbolic links.

I am wondering if there is an easy way to determine if the link is broken (points to a non-existent file) in this scenario.

Here is my code:

FILES=`find /target/ | grep -v '\.disabled$' | sort`

for F in $FILES; do
    if [ -L $F ]; then
        DO THINGS
    fi
done
Cinch answered 8/11, 2011 at 10:39 Comment(0)
D
64
# test if symlink is broken (by seeing if it links to an existing file)
if [ ! -e "$F" ] ; then
    # code if the symlink is broken
fi
Downe answered 8/11, 2011 at 10:45 Comment(7)
Note that the code will also be executed if the file does not exist at all. It is fine with find but in other scenarios (such as globs) should be combined with -h to handle this case, for instance [ -h "$F" -a ! -e "$F" ].Englishism
You're not really testing the symbolic link with this approach.Daph
@Englishism There is no difference.Hitch
@Hitch wrong... Please test your assumptions before commenting: git.io/fx95w This answer is INCORRECT.Vandal
Didn't work for me. I created a link, then I removed the target file and the check was still returning trueSpecial
@Camilo solution worked for me in a more general case (not with find)Oddfellow
FYI all: see man test or man [ for the information on what -e means. Note that in bash, [ itself is a command. Therefore, ! is the first arg to it, -e is the 2nd arg to it, "$F" is the 3rd arg, ] is the 4th arg. That's why spaces are required after the [ and before the ]--the first is the command name and the latter is a required argument, and commands and arguments must always be separated by spaces.Lutenist
W
39

This should print out links that are broken:

find /target/dir -type l ! -exec test -e {} \; -print

You can also chain in operations to find command, e.g. deleting the broken link:

find /target/dir -type l ! -exec test -e {} \; -exec rm {} \;
Weinrich answered 8/11, 2011 at 10:51 Comment(2)
This doesn't work well if you are actually running lots of code. Do you have a if solution ?Sporogonium
Wouldn't find -L foo -type l do better? From a quick test it does work and is much simpler. Of course your answer shows more features of find which is a nice thing. Disclaimer: I've been awake since stupid o'clock so maybe I missed something.Numeral
M
12

this will work if the symlink was pointing to a file or a directory, but now is broken

if [[ -L "$strFile" ]] && [[ ! -a "$strFile" ]];then 
  echo "'$strFile' is a broken symlink"; 
fi
Mantel answered 4/5, 2014 at 23:46 Comment(5)
wouldn't the -L "$strFile" suffice? From the docs: [-L FILE ] True if FILE exists and is a symbolic link. (source.Parenthood
@Sosi could this be required in an old bash version? 2014 vs 2019, I could even dont have that version anymore :)Mantel
right! that's a great point! I didn't notice how old this was, and now I feel bad for digging it upParenthood
@Sosi I think there is no problem, an updated app version's answer is always better I guessMantel
@Sosi We want to test if the symbolic link is working, -L is true even if the file is just a broken symlinkSporogonium
G
5

This finds all files of type "link", which also resolves to a type "link". ie. a broken symlink

find /target -type l -xtype l
Glossy answered 24/5, 2014 at 13:2 Comment(3)
variant: find -L /target -type lMarchellemarcher
Can't you have a symlink to a symlink that isn't broken?'Daph
GNU find and -xtype resolves the symlink all the way to the end. If a symlink is good it will always resolve to something that is not a symlink. Same with -L in GNU find and BSD find.Macdougall
P
2

If you don't mind traversing non-broken dir symlinks, to find all orphaned links:

$ find -L /target -type l | while read -r file; do echo $file is orphaned; done

To find all files that are not orphaned links:

$ find -L /target ! -type l
Pebbly answered 8/11, 2011 at 10:55 Comment(2)
But this follows non-broken symlinks to directories, which we may not want.Mathison
/target could be a file in which case directory traversal isn't a problem.Macdougall
A
1

On recent versions of bash (5.1.16) test -e "$link" or [[ -e "$link" ]] do not behave as expected, failing to recognize the link as broken.

I suggest using readlink(1) as well as testing for the existence of the link as follows:

if [[ ! -L "$link" ]]; then
    echo "Missing or not a symlink"
else
    readlink -q "$link"
    if [[ $? -ne 0 ]]; then
         echo "Broken"
    fi
fi
Allover answered 23/9, 2023 at 23:57 Comment(0)
D
-1

What's wrong with:

file $f | grep 'broken symbolic link'

Decimate answered 31/5, 2019 at 15:54 Comment(1)
There's a few things wrong with it: 1) it follows a working symbolic link and, if the target is a file, will read the contents to try to determine its type; 2) how the output is worded may not be the same everywhere and forever; 3) if the system it's run on is in a locale other than English, it may not print the English message. Add -h flag to fix the first problem, and prefix it with LANG=C to fix the third. You may or may not consider the second problem an acceptable risk.Aprilette
B
-1

If it does qualify as a symbolic link, but is „not existing“, its a broken link.

if [[ -h $link && ! -e $link ]] ; then
    _info "$link is a BROKEN SYMLINK"
fi

REFERENCE

Bowknot answered 17/8, 2020 at 14:8 Comment(1)
-h is described as True if file exists and is a symbolic link. If -h is true then -e is true.Macdougall

© 2022 - 2024 — McMap. All rights reserved.