I frequently move directory trees to other locations or copy their tarballs to other machines, and I would like to have a method to check whether any symlinks in a directory tree A point to locations outside of A since these will be broken in the moved / copied directory.
Use bindfs to create another view of that directory tree.
mkdir /tmp/view bindfs /some/directory /tmp/view
symlinks -r /tmp/view | sed -n 's/^\(absolute\|other_fs\): //p'
(Note that parsing the output assumes that your symbolic links and their targets do not contain newlines, nor do paths to symbolic links contain the substring
-> .) That same utility can also convert absolute symlinks to relative (but you'd want to do that from the original location).
cd -P -- "$dir" for i (**/*(ND@)) [[ $i:A = $PWD/* ]] || [[ $i:A = $PWD ]] || print -r -- "$i => $i:A"
Now, if the directory is
/foo and you have
/foo/bar that's a symlink to
/foo/baz, that's a link whose target is in /foo, but once moved, the link will still be broken, so you may want also to match symlinks to absolute paths.
But even then, a
bar => ../foo/baz in
/foo would be an issue (false negative), so would a
a => b where
b is a symlink outside the tree (false positive, depending on how you want to look at it)
I had to tweak a little the answer given by @bahamat to make it work.
The version provided simply reported the offending absolute location but not the symlink that points to it.
Here's what I used (I am sure it can be improved):
for f in $(find . -type l ); do echo -n $(realpath $f) && echo -n "|" && echo $f ; done | grep -v "^$(pwd)" | cut -d \| -f 2
GNU coreutils provedes
realpath, which resolves symlinks. With this, you could compare each symlink's target to the current working directory with something like:
#!/bin/bash find . | while read filename do if realpath $filename | grep -E "^$PWD" > /dev/null then echo 'this file is safe' else echo 'this file links externally' fi done