96

I am working through SSH on a WD My Book World Edition. Basically I would like to start at a particular directory level, and recursively remove all sub-directories matching .Apple*. How would I go about that?

I tried

rm -rf .Apple* and rm -fR .Apple*

neither deleted directories matching that name within sub-directories.

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
codedog
  • 1,063

5 Answers5

112

find is very useful for selectively performing actions on a whole tree.

find . -type f -name ".Apple*" -delete

Here, the -type f makes sure it's a file, not a directory, and may not be exactly what you want since it will also skip symlinks, sockets and other things. You can use ! -type d, which literally means not directories, but then you might also delete character and block devices. I'd suggest looking at the -type predicate on the man page for find.

To do it strictly with a wildcard, you need advanced shell support. Bash v4 has the globstar option, which lets you recursively match subdirectories using **. zsh and ksh also support this pattern. Using that, you can do rm -rf **/.Apple*. This is not POSIX-standard, and not very portable, so I would avoid using it in a script, but for a one-time interactive shell action, it's fine.

Shawn J. Goff
  • 46,081
  • 4
    I can get find . -type d -name .Apple* to work - it lists all the folders. However, it fails when I add -delete at the end. It just comes back with the usage summary. It's running on BusyBox v1.1.1. Does that make a difference? – codedog Oct 30 '11 at 01:33
  • Make sure to either quote or escape the *, otherwise the shell will expand it, and find will not see it. And as I understood the question, you don't want to get the directories, so make sure to include ! before -type d to tell find not to match directories. – Shawn J. Goff Oct 30 '11 at 04:54
  • Whoops... I was a bit ambiguous. I do want to delete just the directories. I will try quoting the wildcard name. – codedog Oct 30 '11 at 08:04
  • 2
    -delete is not POSIX either. ** was introduced by zsh in the early 90s. It's now (by chronological order of appearance) also in ksh93, fish, bash and tcsh. – Stéphane Chazelas Sep 10 '14 at 20:19
  • 8
    +1 for rm -rf **/.Apple* – Andriy Boyko Jul 13 '16 at 12:37
  • 1
    @codedog, if -delete doesn't work use -exec rm -f {} + instead. – Wildcard Nov 15 '16 at 03:36
  • 1
    Can you get find to be verbose as it deletes? I suppose the "globstar" method would do this by adding the -v switch. – Demis Feb 27 '18 at 22:02
  • Demis - yes. if you include -print at the end, it will tell you what files it deleted. For example find . -type f -name ".Apple*" -delete -print – twasbrillig Oct 17 '18 at 20:22
  • rm -rf **/.DS_Store is really the one every Apple user wants – Ahi Tuna Mar 20 '23 at 13:55
29

I ran into problems using find with -delete due to the intentional behavior of find (i.e. refusing to delete if the path starts with ./, which they do in my case) as stated in its man page:

 -delete

Delete found files and/or directories. Always returns true. This executes from the current working directory as find recurses down the tree. It will not attempt to delete a filename with a "/" character in its pathname relative to "." for security reasons.
Depth-first traversal processing is implied by this option.
Following symlinks is incompatible with this option.

Instead, I was able to just do

find . -type d -name 'received_*_output' -exec rm -r {} +

For your case, it seems that quoting the glob (asterisk, *) was the solution, but I wanted to provide my answer in case anyone else had the similar problem.

NOTE: Previously, my answer was to do the following, but @Wildcard pointed out the security flaws in doing that in the comments.

find . -type d -name 'received_*_output' | xargs rm -r
Pat
  • 849
  • 2
    No good reason to use xargs here. -exec rm -r {} \; or, better, -exec rm -r {} + will do as well without failing on special character filenames. See Why is looping over find's output bad practice? – Wildcard Nov 15 '16 at 03:37
  • 1
    That's good to know. Not worth a downvote, IMO, since the command still gets the job done, but the performance optimization is appreciated. – Pat Nov 16 '16 at 02:05
  • 1
    If it were just a performance optimization I would agree with you, but it's not. It's a security hole. You're not correctly handling files with whitespace in their names, and a maliciously crafted filename will result in data loss. Also see Security implications of forgetting to quote a variable in bash/POSIX shells; some of that is applicable and it may at least give you some idea of the type of issues I'm talking about. – Wildcard Nov 16 '16 at 02:22
  • Try mkdir -p $'blah\nDocuments\n/etc\n/home\n/received__output' in your directory and then run the same command again, if you don't believe there's potential for data loss. Or, since the question is about Mac OS, mkdir -p $'blah\nDocuments\n/Users\n/Applications\n/received__output'. (Warning: You WILL delete all your files if you do this. That's kind of my point.) – Wildcard Nov 16 '16 at 02:27
0

Try:

shopt -s dotglob           # using Bash
printf '%s\n' ./.Apple*    # test
#rm -rf ./.Apple*    
jan
  • 1
-1

What is possible is that there is nothing wrong with your rm/find command, but that the user you are logged in with doesn't actually have delete permissions. Use ls -l to list things with their permissions, and you can identify who you are with the id and groups commands (either should be available). If you are the "wrong user" to do this, you would need to either change the permissions/ownership of the files, or switch to another user.

-3

Simple command:

rm `find ./ -name '.Apple*'` -rf

Good luck!

KimKha
  • 103