12

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 5

7

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

E.g.:

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

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 
1
  • 1
    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) Commented Jan 19, 2017 at 13:50
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)

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
1
  • 1
    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" Commented Oct 4, 2012 at 21:38

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.