0

I ran

/bin/find /home/user/myfiles \! -name '.htaccess' -exec rm -r {} \;

and it removed everything in myfiles and the myfiles directory itself if .htaccess doesn't exist.

What I was expecting is that it would find and remove files within the directory, except for any .htacess file.

What did I do wrong?

6
  • Does this answer your question? No such file or directory With find -exec rm -f {} \; Aug 31, 2022 at 16:47
  • 4
    If you expected it to do something specific other than removing everything, then please mention this in the question. If you left something out of your command, consider putting it back in again for the sake of this question. The command that you currently display is applying rm -r on everything it finds, so it is not at all strange that it removes everything. I would also expect it to possibly emit error messages when it tries to enter directories that it has already deleted.
    – Kusalananda
    Aug 31, 2022 at 17:45
  • 1
    I am voting to close. As OP has told us that they have intentionally left out information that they know to be relevant. If they add it then we should re-open. Aug 31, 2022 at 19:06
  • Okay I will add in the removed portion of the command that is working fine.
    – Mark Lane
    Aug 31, 2022 at 20:09
  • I’m confused.  Is the command with the \! -name clause “working fine” or is it removing the entire directory tree? Aug 31, 2022 at 22:03

4 Answers 4

3

If your version of find supports -delete and -mindepth (GNU find, or the version on macOS Monterey, or a few others), you can use those:

/bin/find /home/user/myfiles -mindepth 1 -delete

If it's some other find, I'd try specifying the path more like this:

/bin/find /home/user/myfiles/*

which globs things beneath the directory you want to leave intact and does the find beneath that.

It's a good idea with tools like find that essentially discover their own arguments for actions to run them with something nondestructive first, like simply listing things or doing an ls on them before deleting them.

2
  • 1
    Be careful with find …/*.  (1) If you haven’t set the dotglob option, it will miss dot-files in the top-level directory.  (2) If you get into the habit of using find …/*, you might feel that it’s OK to use find *, and that can fail if you have files whose names begin with -.  And, while find -- * appears to work, it doesn’t offer you the protection you might expect. Aug 31, 2022 at 21:16
  • Good points all, G-Man. My real recommendation for people with a find lacking those two features would be to get a better find, but I realize that's not always an option. Sep 6, 2022 at 17:42
2

find starts its search at the starting directory itself. That is, if you have

foo/
    a
    b

and you run

find foo

you'll get

foo
foo/a
foo/b

So, when you do -exec rm -r {} \;, it deletes even the starting directory. What you can do instead is tell find not to include the starting directory:

find /home/user/myfiles ! -path /home/user/myfiles -exec rm -r {} \;
8
  • 2
    While you're at using the non-standard -not predicate (standard equivalent is !), you might as well use -mindepth 1 to exclude the start directory (depth 0) Aug 31, 2022 at 16:28
  • Ah! Didn't know about that (nor that -not was non-standard). If you post that as an answer, I'll upvote it. Aug 31, 2022 at 16:29
  • Is -not a GNU thing? Aug 31, 2022 at 16:39
  • 1
    /bin/find /home/user/myfiles the suggestion to use -mindepth 1 works and is the correct answer.
    – Mark Lane
    Aug 31, 2022 at 18:00
  • 1
    @DanielWalker, not just GNU, at least Busybox and FreeBSD also support -not (and -and, and -or, though amusingly the FreeBSD man page is a bit unclear on if it supports the standard -a (though strictly it's unnecessary anyway)).
    – ilkkachu
    Aug 31, 2022 at 20:09
1

Daniel Walker’s answer explains what happened with your command and one way to fix it (and Stéphane Chazelas provided another option in a comment).  It’s good to know how to use find.  But an alternative approach to getting this particular job done, if you are using Bash, is

(shopt -s dotglob  &&  cd /home/user/myfiles  &&  rm -r -- *)

You don’t need find to do a recursive delete, since rm has that capability built-in.

1
  • I am excluding other files that's why I am using find.
    – Mark Lane
    Aug 31, 2022 at 18:05
0

You told it recursively remove /home/user/myfiles/ /home/user/myfiles/a /home/user/myfiles/b you did not tell it to remove /home/user/myfiles/c. However, telling it to recursively remove /home/user/myfiles/ is enough to remove everything.

The exclude (of find) does not say don't delete it. It says don't tell the command (rm) about it.

You must log in to answer this question.

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