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.
5 Answers
You want a program called realpath, used in conjunction with find.
E.g.:
find . -type l -exec realpath {} \; | grep -v "^$(pwd)"
- 39,666
- 4
- 75
- 104
-
1The incantation you provide only reports the offending target location, not the symlink that points to it. As such I have removed my acceptance. Please see my "answer" below: http://unix.stackexchange.com/a/308899/24044 and if you are happy with it (or improve it) I will delete my "answer" and accept your question again. – Marcus Junius Brutus Sep 09 '16 at 16:09
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
- 4,587
- 11
- 44
- 65
-
I found this to be the most useful script:
for f in $(find . -type l); do echo $(realpath -m -q $f) '<-' $f; done | grep-v "^$(pwd)"(most notably-mand-qwhich filters out broken and non-external links) – Adam Lindberg Jan 19 '17 at 13:50
With zsh:
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)
- 544,893
Use bindfs to create another view of that directory tree.
mkdir /tmp/view
bindfs /some/directory /tmp/view
Then use the symlinks utility (shipped by many distributions, or compile it from source) to detect cross-filesystem links.
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).
- 829,060
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
- 231
- 1
- 6
-
1Several problems: no
-type l, no-roption toread, IFS not sanitized forread,$filenamenot quoted,$PWDtreated as a regular expression, paths with newline characters not accounted for,/foobarwould be matched for$PWD== "/foo" – Stéphane Chazelas Oct 04 '12 at 21:38