10

I want to exclude some large content directory

I'm Using it to chown Directory

chown -R admin /home/admin/web/public_html

is there anyway to exclude a subdirectory under html

Like:

chown -R admin exclude=/home/admin/web/public_html/content /home/admin/web/public_html

Something like that

Thanks

7 Answers 7

5
chown -R admin $(ls -I content)

ls -I will list all except specified pattern.

1
  • 7
    Warning: this answer is badly flawed. Do not use it. Maybe unless you understand its flaws and can tell they won't do any harm in your particular case. But then it's still like using a gun as a hammer: not a wise idea, even if unloaded. The flaws: (1) Parsing ls is wrong. (2) Bash pitfall number 1 (you have chmod instead of for but the problems are the same). (3) Possibly "argument list too long". Aug 5, 2020 at 17:26
5

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.

2

You can exclude directories by using !(enumeration_of_directories) notation. Example removing media and logs from chown

chown -R myuser:mygroup /www/myapp/!(media|logs)
3
  • 1
    Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Sep 17, 2022 at 10:52
  • Thanks, this worked for me. More information about this technique can be found here: askubuntu.com/questions/889744/…
    – Charon ME
    Feb 23, 2023 at 11:59
  • 1
    Using the !(...) syntax also requires the following shopt setting: shopt -s extglob. See askubuntu.com/a/889746/132098
    – Abdull
    Mar 30, 2023 at 12:41
1

A somewhat unconventional approach: firejail.

Firejail is a SUID program that reduces the risk of security breaches by restricting the running environment of untrusted applications using Linux namespaces and seccomp-bpf. It allows a process and all its descendants to have their own private view of the globally shared kernel resources, such as the network stack, process table, mount table.

firejail --noprofile --quiet --blacklist=/home/admin/web/public_html/content chown -R admin /home/admin/web/public_html

It's a quite convenient generic way to protect some file(s). In this case, if /home/admin/web/public_html/content is a directory then chown will see an empty "impostor directory" instead. The tool will be able to act on it, so there will be no error. The real directory will stay unaffected.

There's a quirk: if chown is /bin/chown and you blacklist /bin/chown or /bin then chown will not run. In such case consider --read-only instead of --blacklist. See man 1 firejail for details.

From sudo firejail … chown … and firejail … sudo chown … I would prefer the latter.

0
find . -type d -not -path /home/admin/web/public_html  -prune -o -print0 | xargs -0 chown admin 

Where -type d filter by directory and -not -path /home/admin/web/public_html exclude the directory.

6
  • Thanks, for your answer. I had try that just like this. find . -type d \( -path contents -o -path admin/logs -o -path admin/data/backup -o -path admin/data/conversion -o -path admin/smarty/cache -o -path admin/data/engine/storage -o -path 78 -o -path 976 -o -path tmp \) -prune -o -print | xargs chown -R admin
    – Mitra
    Mar 20, 2018 at 14:19
  • But Its also taking long time
    – Mitra
    Mar 20, 2018 at 14:20
  • Should I add -not
    – Mitra
    Mar 20, 2018 at 14:22
  • 1
    If you have a long list then a better approach is to create a file and grep the result. So let say that the exclusion list is in the zzNotNowChownPlease file. Then: find . -type d |grep -fv zzNotNowChownPlease |xargs chown -R admin Should work. Just test the find+grep before.
    – emaV
    Mar 20, 2018 at 14:39
  • 1
    The command is a no-op. The first object tested by find . is .. For it -type d is true, -not -path … is true, -prune is true, -print0 is not evaluated. Nothing more is tested because of -prune for .. Thus the stdin of xargs is empty and the tool runs just chown admin which is invalid. Besides, in general if you start from . then each and every path tested by find will start with ./ or be exactly .; so I can tell in advance that -path /home/admin/web/public_html will always be false (in other words: -path does not perform realpath before testing). Aug 5, 2020 at 18:20
-1

for chown -R exclude subdirectory,but not the parent directory self

eg:

find . -mindepth 1 -maxdepth 1 \( -path ./node_modules \) -prune -o -print0 |xargs -0 chown -R www-data:www-data

exclude . and chown subdirectories and files exclude 'node_modules' folder.

3
  • 1
    -1 How does this answer improve on what has already been posted in the past 3 years since this question was originally posted? Jun 17, 2021 at 3:46
  • @Giacomo1968 it works for the purpose : i want to chown recursively but exclude specific sub directory. mindepth 1 prevent the parent directory , which if included the -R option will cause chown for all subdirectories, so fail for exclude sub directory. maxdepth 1 only first level sub directory and files for result, with -R option chown , it will exec exactly what we want. -print0 ok for debug or dryrun
    – Nick Fan
    Jun 17, 2021 at 3:56
  • 1
    Well, happy this works for you. But this is not an answer that adds new insight that the other answers provide. Please remember this site is not a message board and just posting a “Me too!” answer with slight variation is not of real value to others. Jun 17, 2021 at 4:05
-2

from /home/admin/web/public_html

find . ! -name "content" | xargs -I {} chown admin {}

should work fine, hope it helps you

1
  • Problems: (1) This will exclude every file named content, possibly somewhere deep in the directory tree, not only the specific directory in question. (2) This will still descend into the "excluded" directory and chown files in it. Usually "to exclude a directory" means "to exclude a directory along with its content". (3) xargs (even with -I) interprets quotes and backslashes. Newline characters, quotes and backslashes may appear in pathnames, they will cause the solution to fail. Aug 5, 2020 at 21:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .