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?

Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • 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 '22 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. – ctrl-alt-delor Aug 31 '22 at 19:06
  • Okay I will add in the removed portion of the command that is working fine. – Mark Lane Aug 31 '22 at 20:09
  • I’m confused.  Is the command with the \! -name clause “working fine” or is it removing the entire directory tree? – G-Man Says 'Reinstate Monica' Aug 31 '22 at 22:03
  • It removes the entire tree but only if .htaccess is not present as it won't remove a non-empty directory. – Mark Lane Sep 01 '22 at 03:17

4 Answers4

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.

Stephen Kitt
  • 434,908
  • 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. – G-Man Says 'Reinstate Monica' Aug 31 '22 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. – Christopher E. Stith Sep 06 '22 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 {} \;
Chris Davies
  • 116,213
  • 16
  • 160
  • 287
Daniel Walker
  • 801
  • 1
  • 9
  • 35
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.

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.