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.
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. :)
-delete
than spawning another process just to run rm
, surely?
Commented
Oct 16, 2023 at 6:54
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.
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.