4

I have a prompt that asks me to delete all the files in a directory that the owner (u) can't r, w, nor x, in one command.

I tried this command:

find data -type f ! -perm -u=rwx -exec rm -f {} \;

... but I think it removes too many files.

4 Answers 4

6

I think you want this, which assumes that you are using GNU find specifically:

find -type f \! -perm /u=rwx -exec echo rm -f {} \;

Note that I added an echo for testing.

If the files that get printed match your expectations, take it out. :)

2
  • 4
    Simpler to use -delete than spawning another process just to run rm, surely? Oct 16, 2023 at 6:54
  • 2
    True that ... I just didn't feel like making too many modifications to the original.
    – tink
    Oct 16, 2023 at 8:17
3

If you're on a Linux system, you likely have GNU find and can follow the example in the other answer, using find data -type f ! -perm /u=rwx -delete.

If you're not on a Linux system, you're likely on a BSD system (like macOS, or one of the open-source BSDs), with a find that does not understand the non-standard /u syntax for the argument to the -perm predicate. In this case, you can use stat on each file to single out the symbolic user permissions and test them against the string --- (which means the user doesn't have any permissions to the file):

find data -type f -exec sh -c '[ "$(stat -f %SHp "$1")" = --- ]' sh {} \; -print

This assumes BSD stat, not Linux stat. The -f %SHp option will format the stat output as the "high" (H, the user bits) symbolic (S) permission bits (p) for the given file. The equivalent with the common Linux stat would be to use -c %f and then match that against the ?0?? shell pattern.

You would change -print to -delete to actually delete files.

A slightly more efficient variant that does not start a new shell for each and every file:

find data -type f -exec sh -c '
    for pathname do
        [ "$(stat -f %SHp "$pathname")" = --- ] && echo rm "$pathname"
    done' sh {} +

Remove the echo to remove files.

1

Disclaimer: I'm the current author of rawhide (rh) (see https://github.com/raforg/rawhide)

With rawhide (rh) you can do:

rh -UUU data 'f && !ur && !uw && !ux'

-UUU unlinks/deletes/removes the matches.

data is a path to search.

The rest is the search criteria:

f means it's a regular file.

!ur means the owner-readable permission bit isn't set.

!uw means the owner-writable permission bit isn't set.

!ux means the owner-executable permission bit isn't set.

The last three could be done a little more concisely with none(0700) instead:

rh -UUU data 'f && none(0700)'

Of course, it will only be able to delete the matches if the user has write permission to the directories containing the matching files. If that's a problem, you might need to incorporate sudo:

    rh -x 'sudo rm %s' data 'f && !ur && !uw && !ux'

-x ... executes the given shell command on each match.

%s is replaced with the matching filepath (like find's {}).

BUT Note that this doesn't work if there are access control lists.

If there are ACLs and the owner of all of the files being checked is the current user, then you can use the access(2) system call to check access properly.

GNU find has -readable, -writable, and -executable which performs real access checks, and rh has ir, iw, and ix for this.

rh -UUU data 'f && !ir && !iw && !ix'

But that only helps if the current user is the owner of all of the files in question. If the files can have ACLs and different owners, then the access(2) method won't help. So hopefully you don't have that situation.

0

With zsh:

rm -- data/**/*(D.^rwx)

You must log in to answer this question.

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