8

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.

7

You want a program called realpath, used in conjunction with find.

E.g.:

find . -type l -exec realpath {} \; | grep -v "^$(pwd)"
  • 1
    The 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: 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 9 '16 at 16:09
2

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).

2

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)

2

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 
  • 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 -m and -q which filters out broken and non-external links) – Adam Lindberg Jan 19 '17 at 13:50
1

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
  • Several problems: no -type l, no -r option to read, IFS not sanitized for read, $filename not quoted, $PWD treated as a regular expression, paths with newline characters not accounted for, /foobar would be matched for $PWD == "/foo" – Stéphane Chazelas Oct 4 '12 at 21:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.