Removing folders with find – strange message

find

mkdir foodir
find . -iname foodir -exec rm -fr {} \;

It does the job, but barfs message:

find: `./foodir': No such file or directory

Best Answer

The exact sequence of events would go something like

  1. create foodir
  2. launch find
    1. find reads the current directory, caches the result and then iterates over it, finding an entry for foodir
    2. find launches the exec command for foodir, deleting foodir
    3. find tries to recurse into foodir (it's in find's internal cache), which no longer exists
    4. find displays a warning that it was unable to recurse into foodir
    5. find continues with the next entry (which in this case likely is the end of the list of directory entries, so it exits having done its job to the best of its ability)

So what you are seeing is perfectly explainable, if somewhat unexpected from an outside perspective.

The caching is almost certainly done to improve performance, saving a potentially large number of system calls as well as quite a bit of potential disk I/O for every file. Without a transactional file system, which is common but not guaranteed, there's no guarantee you wouldn't hit issues like this even if find did read the directory once for each entry; and in the case of non-local file systems in particular, even the order of the entries might change between checks, so you can't simply keep track of an index either but must keep track of every directory entry you have visited. For a large directory hierarchy, that becomes prohibitive very quickly.

In general, this is termed a "race condition": a precondition for a calculation changing between when the calculation is done and when the resulting value is used.

Looking at the man page for GNU find, there is an option -ignore_readdir_race which might help to suppress the warning. However, I don't know how much this does for any other commands executed through find. Depending on your needs, that may be sufficient. You can also suppress any errors and warnings from find by redirecting its standard error to /dev/null (append 2>/dev/null to the command line), but I do not recommend that since it can hide more serious errors. I also don't know off hand how that would interact with any error output from the invoked command.

Related Question