Quantcast
Channel: User Kamil Maciorowski - Super User
Viewing all articles
Browse latest Browse all 665

Answer by Kamil Maciorowski for How to "find" while following specific symlinks on Linux?

$
0
0

The last sentence of the question suggests the problem is not with following symlinks in general (and e.g. applying -newer to the symlink in one case, while to the target in another case), but with following symlinks to directories and thus descending where you don't want.

Per specification, find shall detect infinite loops, so a livelock shouldn't happen. Resources (including time) being wasted may be a problem and I understand your concern.

First of all, use -L to allow find to follow symlinks you want it to follow. Then you need a way to detect other symlinks.

In find -L-type l is never true, but you can detect symlinks with -exec test -L {} \; (remember -exec … \; is also a test, it succeeds iff the inner command exits with status 0). This way you still can apply some test(s) specifically to symlinks and -prune (or not) accordingly.

For example the following command will descend only symlinks whose names start with foo or end with bar:

find -L . -exec test -L {} \; ! \( -name 'foo*' -o -name '*bar' \) -prune -o -print

Whatever expression you originally wanted, place it after -prune -o. In the above command the "original" expression is -print. Remember -exec suppresses the implicit -print, so if you need -print then add it explicitly (like we did).

Note that -name tests the name of the symlink itself. If you want to test the name of the target then you need realpath, possibly basename and some shell code; example:

find -L . -exec test -L {} \; -exec sh -c '   case "$(basename "$(realpath "$1")")" in      baz* ) exit 0 ;;      *qux ) exit 1 ;;   esac   exit 0' find-sh {} \; -prune -o -print

The above command will -prune any symlink to any file whose name starts with baz, but it will descend symlinks to directories with names ending with qux (unless starting with baz) and finally -prune all other symlinks.

Notes:

  • realpath and basename are not portable.
  • realpath resolves all symlinks.
  • The behavior in case of a broken symlink may or may not be what you expect.
  • Invoking a separate test for every file will hurt performance, but I have found no better way. Invoking sh, realpath and basename is also bad in this matter, but here it happens only for symlinks, so in the majority of cases the impact will be limited.
  • find-sh is explained here: What is the second sh in sh -c 'some shell code' sh?
  • In the examples pathnames that get -pruned will not be -printed. Change the final fragment to -prune -false -o -print and they will be printed. See explanation; a portable alternative to -false is there in case you need it.

Viewing all articles
Browse latest Browse all 665

Trending Articles