254

I issue the following command to find the .svn directories:

find . -name ".svn"

That gives me the following results:

./toto/.svn
./toto/titi/.svn
./toto/tata/.svn

How could I process all these lines with rm -fr in order to delete the directories and their content?

10
  • 5
    GNU find has the -delete option.
    – Marco
    Commented Sep 9, 2013 at 8:49
  • 5
    Or you can add -exec rm -r "{}" \; to the end of the find - be careful when using rm -r! :)
    – Drav Sloan
    Commented Sep 9, 2013 at 8:52
  • 19
    @Marco The delete option does not seem to work on directories.
    – Arnaud
    Commented Sep 9, 2013 at 9:01
  • 1
    @SuperChafouin but will not work for paths with spaces in them (hence using -exec with quoted "{}").
    – Drav Sloan
    Commented Sep 9, 2013 at 9:55
  • 1
    @DravSloan:  Putting {} in quotes has no effect. Commented Oct 27, 2020 at 23:43

9 Answers 9

299

Find can execute arguments with the -exec option for each match it finds. It is a recommended mechanism because you can handle paths with spaces/newlines and other characters in them correctly. You will have to delete the contents of the directory before you can remove the directory itself, so use -r with the rm command to achieve this.

For your example you can issue:

find . -name ".svn" -exec rm -r "{}" \;

You can also tell find to just find directories named .svn by adding a -type d check:

find . -name ".svn" -type d -exec rm -r "{}" \;

Warning Use rm -r with caution it deletes the folder and all its contents.

If you want to delete just empty directories as well as directories that contain only empty directories, find can do that itself with -delete and -empty:

find . -name ".svn" -type d -empty -delete
10
  • 41
    I have seen advice to always run -type after -name in find commands, since calls to stat to get the type are expensive. I just tried it myself on a fairly large bunch of files, and it seems to be true: running find . -name 'foo' -type d took 19 secs, while find . -type d -name 'foo' took 32 secs. So about 50% longer time to run -type first. Commented Mar 16, 2015 at 16:37
  • 14
    i've been using this command for years, but on Mac now i get errors saying those directories do not exist. Even though it does delete them. I never saw messages before.
    – chovy
    Commented Dec 23, 2015 at 8:51
  • 2
    Same as @chovy. Is there a way to get rid of these messages?
    – Clément
    Commented Apr 23, 2016 at 15:32
  • 4
    @chovy @clément That is because find wants to see in that folder for other matches, while it's removing the folder at the same time. ~I don't know yet how to fix this.~ Dirty fix: find . -name "folder-to-delete" -print0 | xargs -r0 -- rm -r
    – Charlie
    Commented Jul 20, 2016 at 12:02
  • 9
    @chovy @Clément I think the -depth argument fixes this: find . -depth -name ".svn" -type d -exec rm -r "{}" \;
    – gimboland
    Commented Jul 27, 2016 at 7:57
107

Here is a portable still faster than the accepted answer way.

Using a + instead of a semicolon as find command terminator is optimizing the CPU usage. That can be significant if you have a lot of .svn sub-directories:

find . -name .svn -type d -exec rm -rf {} +

Note also that you never1 need to quote the curly braces here.

1 Unless you use the fish shell (this might have been fixed since I wrote this reply).

13
  • 6
    What's difference between + and semicolon? Why we don't use curly braces? Commented Jan 4, 2017 at 19:25
  • 3
    @ShichengGuo With the semicolon, there will be one rm command per directory found, with the + a single rm command will process all directories found (or at least a very large number of them.) I don't get your second question, curly braces are used here.
    – jlliagre
    Commented Jan 4, 2017 at 20:13
  • I think @ShichengGuo means why don't we need to quote the curly braces here (@jlliagre wrote that we never need to quote them). I can't find a reference now, but I understand it's because find will automatically escape the paths replaced for {}. Commented Mar 8, 2017 at 6:47
  • 2
    @gaoithe I did rollback you edit which replaced a correct statement with an incorrect one. Using + does reduce CPU usage, Using ; does not lead to a command too long error.
    – jlliagre
    Commented Mar 22, 2017 at 12:26
  • 1
    This should definitely be the accepted answer! Working in 2022 in bash. Commented Oct 8, 2022 at 5:51
32

Assume you are using gnu find, you can use the -delete option:

find . -name test -delete

which is easier to remember.

7
  • Consider expanding your post with an explanation of the command (or documentation to back up your solution). Often one (or two) line answers are not the most illuminating.
    – HalosGhost
    Commented Aug 10, 2014 at 23:02
  • 156
    This doesn't work on non-empty directories.
    – belacqua
    Commented Dec 16, 2014 at 20:47
  • 2
    also works on Mac OS X
    – draw
    Commented Mar 29, 2016 at 3:11
  • This does not work for non-empty folder but it's the easier & safer solution Commented Feb 7, 2018 at 16:01
  • 4
    The options order is very important, find will execute them in order so, -delete have to be the last one Commented Mar 6, 2018 at 7:25
18

A faster way to do this is:

find . -name ".svn" -type d -prune -exec rm -rf '{}' '+'

In case you have ".svn" inside another ".svn".

2
  • 1
    That's not very useful if you want to find the directories to delete with the sub-directories. Commented Aug 1, 2018 at 7:47
  • 2
    using -prune is the most accurate way. Thanks! Commented Jun 27, 2020 at 15:10
15

On my computer when I use:

find . \( -name dirname -type d \) -exec rm -r '{}' ';'

The directories are deleted but I get the error:

find: ‘./dirname’: No such file or directory

for each directory.

My directories aren't empty, so the -delete option won't work for me. I found the reason for this behavior here:

  1. find takes (not necessarily) the first entry in the ./ directory. this would be e.g. dir.1/
  2. it compares it to the pattern 'dir.?'. does it match? yes.
  3. find executes "rm -r dir.1".
  4. find tries to enter dir.1/ to find the pattern within the directory. it doesn't know anything about the exec command.
  5. it doesn't find dir.1/ anymore. returns ENOENT (look at the strace output)

I used this instead to work around:

rm -r `find . -name dirname -type d`

Keep in mind that find will still try to recurse into directories named dirname, which isn't really necessary and will take some additional time. Depending on your directory structure, you might be able to work around this with the --depth find option. In addition, if you have a directory structure like dirname/foo/dirname you will get "No such file or directory" errors from rm. To suppress the errors you can redirect stderr to /dev/null or use the -f (force) flag with rm.

4
  • 4
    Bad idea: file name with spaces will cause all sorts of horrible issues
    – Clément
    Commented Apr 23, 2016 at 15:31
  • 5
    For future readers: find . -name "to-delete" -print0 | xargs -r0 -- rm -r is a failproof version which doesn't crash on spaces
    – Charlie
    Commented Jul 20, 2016 at 12:08
  • 5
    See unix.stackexchange.com/a/115869/8257 you need to add -prune.
    – user8257
    Commented Jan 29, 2017 at 1:59
  • 2
    Add -prune to avoid the "No such file or directory" error. Commented Feb 24, 2017 at 16:25
7

Bash specific solution:

shopt -s globstar
rm -r **/.svn
shopt -u globstar #optional. this will disable globstar expansion
2
  • 4
    Note for command line expansion of globs that match many files, there is a limit to the number of files you can match with this mechanism. Going over this limit will result in bash: /bin/rm: Argument list too long
    – Drav Sloan
    Commented Sep 9, 2013 at 10:06
  • 1
    @DravSloan is correct, but that limit is in the hundreds of thousands of files. It's something to bear in mind, but probably won't be a problem for most people.
    – evilsoup
    Commented Sep 9, 2013 at 10:26
7

I've found that the -delete action does work nicely with the -path test. For instance, the following ought to work on the original posters problem:

find . -path '*/.svn*' -delete

Note that in addition to deleting '.svn' directories (and their contents), this will also delete any files or directories whose names start with '.svn'. For example, if you used -path '*/.git*' it would also delete '.gitignore' and '.gitattribute' in addition to '.git/'. To avoid that, and just delete directories with that exact name use:

find . -path '*/.svn/*' -or -name '.svn' -delete

Note the slash after .svn. This will first find, and delete, all the files under '.svn', then delete the .svn directory itself.

4
  • Are you sure? -delete implies -depth, and it sure deletes non-empty directories on my system.
    – Magnus
    Commented Dec 20, 2017 at 13:56
  • Retested and it works. Maybe it was just typo which I've corrected.
    – kenorb
    Commented Jan 5, 2018 at 22:23
  • I also tried this approach and it worked - the directories were deleted.
    – A.L.
    Commented Apr 6, 2018 at 8:38
  • I’m positive that, for me, -delete won’t delete non-empty directories (GNU find 4.6.0). Seems to be the case for many people. What is your implementation of find?
    – Maëlan
    Commented Dec 10, 2021 at 1:13
5

TLDR

find . -name .svn -prune -execdir rm -rf {} +

Details

Use -execdir, not -exec

From man find:

There are unavoidable security problems surrounding use of the -exec action; you should use the -execdir option instead.

In most case, -execdir is a drop-in replacement for -exec.

Use +, not ;

From man find:

As with the -exec action, the `+' form of -execdir will build a command line to process more than one matched file, but any given invocation of command will only list files that exist in the same subdirectory.

When looking for an exact name match, + and ; will do the same, as you cannot have two files with the same name in the same directory, but + will provide increased performance when several files/directories match your find expression within the same directory.

Also, ; needs escaping from your shell, + does not.

Use -prune

From man find:

-prune: True; if the file is a directory, do not descend into it.

This avoid searching a directory that we want to delete. Obviously, it needs to be put after the name test. See:

$ mkdir -p test/foo/bar
$ find test -name foo -execdir rm -rf {} +
find: ‘test/foo/bar’: No such file or directory

Versus:

$ mkdir -p test/foo/bar
$ find test -name foo -prune -execdir rm -rf {} +
# no error
2
  • 1
    I’d use -prune rather than -depth. There is no point in having find recurse under directories you are going to delete anyway.
    – Maëlan
    Commented Dec 10, 2021 at 1:17
  • 1
    Using -execdir (rather than -exec) cancels all the benefit of using + (rather than ;). From the man page again: “As with the -exec action, the + form of -execdir will build a command line to process more than one matched file, but any given invocation of command will only list files that exist in the same subdirectory.” In our case, two occurrences can never lie in the same parent directory, since both occurrences have the same base name. Security is not a concern for a command line that you type yourself in a terminal once, with a known command being -executed.
    – Maëlan
    Commented Dec 10, 2021 at 1:34
1

Short and simplest solution is to use xargs here

find . -name ".svn" | xargs rm -rf
1
  • 2
    This will fail if any path to a .svn directory includes whitespace. The other answers avoid this problem. Commented Aug 17, 2021 at 13:27

You must log in to answer this question.

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