How to make find follow most, but not all, symbolic links

findsymlink

My OS and home directory are on an SSD. I store some large files on a hard disk, and symlink from my home directory to the hard disk (eg. ~/Videos/Films is a symlink to /mnt/hdd/Films). I also have a number of Wine prefixes, each of which has dosdevices/z: symlinked to /.

So, if I used find without -L, it will miss everything that's on the hard disk. But if I use find with -L, it ends up in a loop due to Wine's symlink to /.

Is there any sensible way of resolving this, so that find searches where I want it to? Intuitively, I'm wanting something along the lines of "follow symlinks, unless they're under a directory called dosdevices". "Follow symlinks that are to something on the hard disk" would work too.

Best Answer

The -prune primary tells find not to recurse under a directory.

find -L -path ~/.wine/dosdevices -prune -o -type f -name 'My Favorite Movie.*' -print

If you want to use -lname in your condition, you can't use the -L option, because -L causes most predicates to act on the target of the link, including -lname. My recommendation in that case would be to use both your home directory and the hard disk root as the roots of your search.

find ~ /mnt/hdd -xtype f -name 'My Favorite Movie.*'

You might run find ~ -type l … to gather a list of symbolic links and use them as additional roots.

( IFS=$'\n'; set -f;
  find ~ $(find ~ -type l -lname '/mnt/hdd/*') \
       -xtype f -name 'My Favorite Movie.*' )

If you really want to recurse under symbolic links, you can exclude specific symbolic links by target instead of by name. However you can only exclude a finite list, not a pattern or subtree this way.

find -L \( -samefile /exclude/this -o -samefile ~/and/that \) -prune -o \
     -type f -name 'My Favorite Movie.*' -print

You can use other criteria such as ! -writable (files that you don't have write permission to), but I don't think GNU find has a way to recurse under symbolic links only if their target text matches a certain expression.

You could build a find command that does almost what you want except for not excluding enough symbolic links, run find2perl to convert the query to a Perl script, and tweak the Perl script a bit. There are many switches of GNU find that find2perl doesn't recognize, however.

Related Question