1

I'm using find to delete files matching some criteria:

find -type f [more criteria] -delete

That works fine, in general, but I'm running into issues with sub-directories created by other users: as far as I understand, whether I'm allowed to delete a file depends on my permissions w.r.t. the parent directory. Hence the command above fails for files contained in a sub-directory created by another user:

fbrucker@host /tmp $ ls -lh
total 4,0K
drwxrwxr-x 2 root root 4,0K Jun 15 11:44 foo/

fbrucker@host /tmp $ ls -lh foo/
total 0
-rw-rw-r-- 1 fbrucker fbrucker 0 Jun 15 11:44 a
-rw-rw-r-- 1 root     root     0 Jun 15 11:44 b

fbrucker@host /tmp $ find -type f -delete
find: cannot delete ‘./foo/b’: Permission denied
find: cannot delete ‘./foo/a’: Permission denied

It's OK that I cannot delete the files, but the missing permissions make find exit with a non-zero status. I'd like to avoid that.

How can I filter out files that I cannot delete before trying to delete them?

Note that simply filtering out the error messages is not enough for me, since that will still leave me with the non-zero exit code. Also, ignoring the exit code completely won't work for me, because I do want catch other errors (my real find command is more complex, for example for find /somedir -type f -delete I'd like to not ignore the error that is raised when /somedir does not exist).

I know that find has tests for the file's permissions (e.g. -perm, -readable, -writable), but I don't think they apply here. For example,

fbrucker@host /tmp $ find -type f -writable
./foo/a

even though I cannot delete foo/a since I don't have the necessary permissions on foo.

What I'm looking for is something like a -deletable test, which doesn't seem to exist.

1
  • In general, the problem with stuff like -deletable is the classic "time of check to time of use" issue. The permissions of a file might change in the middle, so that -deletable would be truthy, and the deletion would still fail. It's better to just try to do it and deal with or ignore the error later.
    – ilkkachu
    Jun 15 at 19:30

2 Answers 2

3

Yes you need to have write access to the parent directory and if that directory has the t bit set, be either the owner of that directory or of the file you want to unlink, and all that assuming other security controls like ACLs are not in place and that the FS the file is on is not read-only which makes the is deletable check more complicated.

For -readable / -writable / -executable (non-standard) find can just use the corresponding access(R_OK/W_OK/X_OK) system call, but there's no equivalent for unlinking a file.

With GNU find 4.9.0 or newer, you can do an approximation with:

find . -depth -type d -writable '(' -uid "$EUID" -o ! -perm -o=t ')' -print0 |
  find -files0-from - -mindepth 1 -maxdepth 1 -type f -delete

(also assuming a shell like zsh or bash that sets $EUID. If not, use "$(id -u)").

That leaves the files you own in t-bit directories you don't own which you'd need to handle separately:

find . -depth -type d -writable ! -uid "$EUID" -perm -o=t -print0 |
  find -files0-from - -mindepth 1 -maxdepth 1 -type f -uid "$EUID" -delete

You'd likely want to set the pipefail shell option as well to be able to detect errors by the first find.

With older versions of GNU find, you can always do:

find . -depth -type d -writable '(' -uid "$EUID" -o ! -perm -o=t ')' -exec sh -c '
  exec find "$@" -mindepth 1 -maxdepth 1 -type f -delete
  ' sh {} +

If the point is to ignore failures to unlink files as long as it's because of access rights, you could do:

set -o pipefail
find . -depth -type f -print0 |
  perl -l -0ne '
    unless (unlink($_) || $!{EACCES}) {
      warn "$_: $!\n";
      $ret = 1;
    }
    END {exit $ret}'
1

One workaround is to use -exec rm {} \; instead of -delete, since find ignores the exit code of the -exec calls.

3
  • 2
    Strictly speaking, It doesn't ignore it. The exit code determines whether the predicate succeeds. So you can do find . -exec rm -f {} \; -printf '"%p" was successfully deleted.\n' for instance. Jun 15 at 14:55
  • 1
    You'd likely want to use the -f option of rm unless you're happy for it to be interactive. Jun 15 at 14:55
  • if you used -exec ... {} +, it would actually ignore the exit code of the executed command. (but you should still use rm -f)
    – ilkkachu
    Jun 15 at 19:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.