453

I'm trying to understand the difference between these two commands:

find / -name .DS_Store -delete

and

find / -name ".DS_Store" -exec rm {} \;

I noticed that the -exec ... {} method is preferred. Why? Which one is safer/faster/better? I've used both on my Macbook and everything appears to work well.

Onion
  • 4,641

4 Answers4

453

-delete will perform better because it doesn't have to spawn an external process for each and every matched file, but make sure to call this option after the filtering criteria (i.e. -name, -cmin, -type, etc.), otherwise it will delete the specified entire file tree. Indeed, quoting the find manpage :

GNU find searches the directory tree rooted at each given starting-point by evaluating the given expression from left to right, according to the rules of precedence (see section OPERATORS), until the outcome is known

For example,

find . -name .DS_Store -type f -delete

should be fine.

It is possible that you may see -exec rm {} + often recommended because -delete does not exist in all versions of find. I can't check right now but I'm pretty sure I've used a find without it.

Both methods should be "safe".

A common method for avoiding the overhead of spawning an external process for each matched file is:

find / -name .DS_Store -print0 | xargs -0 rm

(but note that there is a portability problem here too: not all versions of find have -print0!)

Celada
  • 44,132
  • 12
    I see. I've also read that using the -delete switch before -name deletes the specified file tree, so I guess I have to be careful. – Onion Nov 13 '14 at 18:56
  • 24
    On recent find you can use -exec rm {} + to remove all matched files with single rm command. – jimmij Nov 13 '14 at 18:59
  • 3
    .DS_Store doesn't contain any special characters at all, so the quotes are unnecessary and change nothing in this case. – Celada Nov 13 '14 at 19:09
  • What are other special characters that could possible cause an unquoted string to be interpreted as two separate commands/switches? – Onion Nov 13 '14 at 19:10
  • 2
    Basically only whitespace (spaces, tabs, maybe others) is the only thing that causes an unquoted string to be interpreted as two separate command line arguments, but that's not all you have to sorry about when deciding whether or not to quote. You have to worry about all shell metacharacters like ; or | or > or < and \ and many others which have special meaning to the shell unless quoted. – Celada Nov 13 '14 at 21:41
  • "but note that there is a portability problem here too: not all versions of find have -print0!" And argument list is not unlimited :-) – Marco Marsala Oct 20 '15 at 21:15
  • 4
    @MarcoMarsala xargs takes care of the limited-size argument list problem transparently by breaking it up into multiple invocations of the command. – Celada Oct 22 '15 at 19:36
  • @Celada is it necessary to put / after find? – Valerio Mar 04 '21 at 09:07
  • 2
    @Valerio The first argument after the "find" command is the base directory to start from, often "." for [the current directory], whereas "/" would start at the full running system root (unless running in a chroot or other such). – killermist Jul 30 '21 at 08:47
58

Assuming .DS_Store represent files and not directories, the most portable still fast way to do it would be:

sudo find / -name .DS_Store -exec rm {} +

The only risk is for sudo not to be available but it is quite low nowadays.

The -delete option used to demand FreeBSD or later GNU find and is still non standard in a few other find implementations, so is not always available.

The command termination + instead of \; highly optimizes the exec clause by not running the rm command for each and every .DS_Store present on the file system. It is more ubiquitous, being specified by POSIX.

jlliagre
  • 61,204
  • 11
    -delete is from FreeBSD from 1996, not added to GNU find until 2004, -exec cmd {} + is from SysV in 80s, standardised by POSIX in 1992, added to GNU find in 2005. – Stéphane Chazelas Feb 09 '22 at 15:07
33

For a machine such as your macbook you won't find much difference in performance between the two commands. However, if you look at the -exec version you can see a subtle difference:

sudo find / -iname ".file-to-delete"  -exec rm {} \;

This means that you will find all those files with name .file-to-delete. However this search might return some unwanted false positives. When doing something with sudo you should be a bit more careful. The advantage of using -exec rm {} is that you can pass arguments to rm like this:

sudo find / -iname "*~"  -exec rm -i {} \;

In this example I want to remove those backup files that emacs makes. However that tilde could be in some obscure file that I don't know about and could be important. Plus I want to confirm the delete. So I put the option -i on the rm command. This will give me an interactive delete.

Also you can refine the usage of rm to delete directories as well as files:

find /usr/local/share/ -iname "useless" -exec rm -r {} \;

In brief, the -exec gives you a bit more control over the actual command that removes the found item. The advantage is that you use one tool to find the files, another tool to remove them. Also not every version of the find utility has the -delete option. So better to use each tool for its proper job. This is the unix philosophy - one tool, one job, use them together to do what you need to do.

AdminBee
  • 22,803
5

In addition to the existing answers, note that -delete will not delete directories if they are not empty. So you need -exec rm -r in these cases.

For example, this doesn't work:

$ find . -type d -name ".TemporaryItems" -delete
find: cannot delete ‘./.TemporaryItems’: Directory not empty

So you need this instead:

find . -type d -name ".TemporaryItems" -exec rm -rv "{}" +

The example adds the -v (--verbose) option to rm.

mivk
  • 3,596