Quantcast
Viewing all articles
Browse latest Browse all 656

Answer by Kamil Maciorowski for chown -R exclude some directory

In Linux the most general tool to do something to files meeting some criteria is find. Few other answers base on the find … | xargs … idea, which is robust only if it uses null-terminated strings. This means find … -print0 | xargs -0 …, but these options are not required by POSIX.

With POSIX find it's usually better to use -exec than to pipe to xargs. Personally I prefer -exec even if I can safely use xargs. Knowing that -exec is also a test, so it can be used to build custom tests (example), it's good to be familiar with it anyway; and then there is no reason not to use it instead of xargs. The real power of xargs is its ability to parse strings with quotes and escaped characters; but this is hardly ever useful when reading from find, almost always it's harmful. Non-POSIX options like -0 and --no-run-if-empty can make xargs a good companion of find … -print0, but still the good POSIX -exec is (almost?) always at least as good.

Your problem can be solved by

find /home/admin/web/public_html \   -path /home/admin/web/public_html/content -prune \   -o -exec chown admin {} +

It works like this: if the path is …/content then do not descend into it (-prune); otherwise (-o) execute chown admin on the file (note: directory is also a file).

Notes:

  • Do not use chown -R here. The first file tested is /home/admin/web/public_html and if you use chown -R on it then nothing will be excluded.

  • -exec chown … {} + can and will pass multiple paths to chown, while -exec chown … {} \; would pass just one (so there would be one chown spawned per file). The syntax with + reduces the number of spawned chown processes, this speeds things up. Even then find will spawn more than one chown process if the number of files is too large for a single command line (compare "argument list too long"). Note it works because chown can take multiple paths; some tools cannot and for them the syntax with + is out of the question.

  • -path matches against the entire path. The path is what find thinks the path is, not necessarily the canonical path. If the starting path is /home/admin/web/public_html then every path tested will start with this string; but if the starting path is ./ then every path tested will start with this string. In the former case -path /home/admin/web/public_html/content will never be true, even if realpath ./ prints /home/admin/web/public_html, because the relevant directory will be identified by the string ./content and this is the string you would want to match with -path. In general you need to adjust the argument of -path to the starting location(s) (or use wildcards maybe).

  • If you need to exclude multiple patterns then follow this example:

    find . \   -path ./content -prune \   -o -path ./foo/bar -prune \   -o -path '*/baz' -prune \   -o -exec chown admin {} +

    which can be compacted to

    find . \   \( -path ./content \      -o -path ./foo/bar \      -o -path '*/baz' \   \) -prune \   -o -exec chown admin {} +

    where parentheses are important.

    With -regex you may be able to combine multiple patterns into one (example). This test is not specified by POSIX though, implementations of find may or may not support it. You tagged ; in Debian find is GNU find, it supports -regex.

  • Other tests (e.g. -name or even -exec) can be used to exclude files.


Viewing all articles
Browse latest Browse all 656

Trending Articles