1

Take the following directory structure and ownership:

userA ./dir1          
userA ./dir1/dir1.1
userA ./dir1/dir1.2
userA ./dir2
userA ./dir2/dir2.1
userA ./dir2/dir2.1/dir2.1.1
userB ./dir2/dir2.2
userB ./dir3

Running find . -type d -user userA will return:

./dir1          
./dir1/dir1.1
./dir1/dir1.2
./dir2
./dir2/dir2.1
./dir2/dir2.1/dir2.1.1

But I am looking to see this output:

./dir1
./dir2/dir2.1
  • dir1.1 and dir1.2 are not returned because no other user owns any files/dirs under dir1.
  • Similarly, dir2.1.1 is not returned.
  • dir2 is not returned (although it's owned by userA) because dir2.2 is owned by userB.

This can be useful for disk cleanup so the user can identify which directories are "safe" to run rm -rf on. When there are billions of files du has a very high runtime. Also limiting the depth of the search doesn't help because the directories could be at any hierarchy level.

5
  • Please give more detailed example of directories ownership.
    – Hellseher
    Jul 31, 2019 at 20:46
  • If dir1's subdirectories are owned by another user, they won't show up in the output. So what's the problem?
    – muru
    Aug 1, 2019 at 0:07
  • Is this "I want to identify the outermost directories whose transitive contents are all owned by user X"? I don't think find is able to do that, nor any possible post-processing of the output. You'd need to build some sort of custom filter, I think. Aug 1, 2019 at 7:08
  • We cannot see; you haven't shown the ownership of the various directories. Please show us.
    – Jeff Schaller
    Aug 1, 2019 at 20:03
  • 1
    Hi Everyone, thanks for your feedbacks. I have modified the question and hope it is more clear now. Sorry about being unclear.
    – Arash A
    Aug 3, 2019 at 1:45

1 Answer 1

0

Use this script:

#!/bin/sh -

u="$1"
shift

find "$@" -type d -user "$u" ! -exec sh -c '
   find "$2" -type d ! -user "$1" | grep -qm 1 .
' find-sh "$u" {} \; -prune -print

Example usage:

/path/to/the_script userA ./

The first argument (here userA) is the user name. Everything that follows (here ./) will be passed to find; you should pass starting path(s) this way (although injecting tests is technically possible).

The script uses main find to find directories owned by the user. For such directory another find is invoked, it finds subdirectories not owned by the user. If there is none, the directory is pruned (so the main find will not check its subdirectories) and its pathname is printed.

Notes:

  • If the inner find finds anything then the outer (main) find will not prune the directory, it will check its subdirectories. If what the inner find found is deep down then many subdirectories on different levels will be checked many times "in vain" before the script moves on to another branch of the directory tree. Unless the directory tree is very big, the OS should cache the metadata and the next find checking the same subdirectory should be fast. Checking multiple times seems suboptimal, but any attempt to optimize this would complicate the code; IMO it's not worth it.
  • grep -m 1 exits after the first match, but this option is not portable. If your grep does not support -m then simply don't use it (i.e. use grep -q .). The script will be slower but it will work.
  • find-sh is explained here: What is the second sh in sh -c 'some shell code' sh?
  • The title says "if nothing is owned by another user" but your example and the use case suggests "if no subdirectory is owned by another user". My script checks (sub)directories only. To check "if nothing …" omit the -type d test in the invocation of the inner find.
  • Permission denied errors may result in false positives.

You must log in to answer this question.

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