48

I need to delete all compiled data:

  • directories called build,
  • directories called obj,
  • *.so files.

I wrote a command

find \( -name build -o -name obj -o -name *.so \) -exec rm -rf {} \;

that goes through all the directories recursively and deletes all I need.

Why do I have such an output at the end? Maybe I should write a different command.

find: `./3/obj': No such file or directory
find: `./3/build': No such file or directory
find: `./1/obj': No such file or directory
find: `./1/build': No such file or directory
find: `./2/obj': No such file or directory
find: `./2/build': No such file or directory
  • on what system you are ? you should always use find like this find /search_directory options omiting the search directory is not a good idea – Kiwy Feb 19 '14 at 09:22
  • Automated deleting like this is a bad idea. You could have a script give you candidates, which you should then look at to make sure you are not deleting anything important or necessary to the system. You aren't explicit where you are running this. If you are only doing this in a user space, I suppose it can't do much harm, but you should make sure you are not doing this in a system area accidentally. You definitely want to run such a script as user. – Faheem Mitha Feb 19 '14 at 09:43
  • @Kiwy,@FaheemMitha, the command will only be used in the project directoty; it will not do any harm there. – Maksim Dmitriev Feb 19 '14 at 10:29
  • Related: https://stackoverflow.com/questions/22462124/find-command-in-bash-script-resulting-in-no-such-file-or-directory-error-only – Ciro Santilli OurBigBook.com Aug 29 '19 at 10:43

3 Answers3

87

Use -prune on the directories that you're going to delete anyway to tell find not to bother trying to find files in them:

find . \( -name build -o -name obj -o -name '*.so' \) -prune -exec rm -rf {} +

Also note that *.so needs to be quoted as otherwise it may be expanded by the shell to the list of .so files in the current directory.

The equivalent of your GNU -regex-type one would be:

find . \( -name build -o -name obj -o -name '*?.so' \) -prune -exec rm -rf {} +

Note that if you're going to use GNU specific syntax, you might as well use -delete instead of -exec rm -rf {} +. With -delete, GNU find turns on -depth automatically. It doesn't run external commands so in that way, it's more efficient, and also it's safer as it removes the race condition where someone may be able to make you remove the wrong files by changing a directory to a symlink in-between the time find finds a file and rm removes it (see info -f find -n 'Security Considerations for find' for details).

find . -regextype posix-egrep -regex '.*/((obj|build)(/.*)?|.+\.so)' -delete
13

I guess the reason is that find deletes the directory tree first and tries to check the directory contents which is obviously not the best possible order. You can force find to check the contents first:

find . -depth ...

You should consider using -delete for files and -exec rmdir for directories.

Hauke Laging
  • 90,279
0

My solution.

find . -regextype posix-egrep -regex ".*/(obj|build|.+\.so)" -prune -exec rm -rf {} +

+ vs \; in the -exec command

  • That's not always going to work, only if no boundary between executions happens inside a deleted directory. – Gilles 'SO- stop being evil' Feb 19 '14 at 23:14
  • @Gilles, what does "boundary between executions" mean? – Maksim Dmitriev Feb 26 '14 at 07:41
  • 1
    -exec … {} + executes the command for multiple files at a time, as many as fit on the command line. If there are too many files (or more precisely if the total length of the paths is too long), find will execute multiple instances of rm, and thus may end up removing a directory that it's traversing. It's just less likely with + than with ;, but + doesn't solve the problem. – Gilles 'SO- stop being evil' Feb 26 '14 at 09:56