find -exec directories with spaces

All, I have a cleanup script that removes directories and all contents underneath, but I am having issues with directories with spaces.

This is the command I am currently running, how can I get it to work with directories with spaces?

find /path -mindepth 3 -type d -exec rm -rf {} \;

Strange, I thought that the find exec arguments were not subject to shell word splitting ...

I cannot reproduce:

$ mkdir a\ b
$ ls -ld a\ b/
drwxr-xr-x+ 1 sysadmin None 0 2010-03-12 19:57 a b/
$ find -depth -name 'a b' -exec rm -r {} \;
$ ls -ld a\ b/
ls: cannot access a b/: No such file or directory

Could you please give more details? Which OS? Could you also copy/paste the command and the output from your terminal?

Running RHEL 5.4
Well, it is finding it, but then it cannot remove because of the space. So, I think the issue is with the remove command and not the find command. May need to pipe it somehow to format it?

Regular find command:

find /home/chroot/marketing/ -mindepth 3 -type d
/home/chroot/marketing/user/download/Fake Name
 find /home/chroot/marketing/ -mindepth 3 -type d -exec rm -rf {} \;
find: /home/chroot/marketing/user/download/Fake Name: No such file or directory

Could you try with -depth:

 find /home/chroot/marketing/ -depth -mindepth 3 -type d -exec rm -rf {} \;

Gave that a try, but get same error. Because there is a space in the Directory the RM command can't find it. It somehow needs a way to escape the space like dir\ dirname

Have you considered using find -print0 | xargs construct?

You can try:

... -exec rm -rf '{}' \;

... but I still don't think that the problem is the space in the directory name.

Rad, It does work for any directories without a space so I know the code works, but only doesn't work for directories with a space.

I'll give your code a try!

Try this:

find /path -mindepth 3 -type d -print0 | xargs -0 rm -rf

Fractionally off topic.
@radoulov and markdjones82 .

After a debate a while back with jlliagre on the subject of quoting '{}' please do post whether quoting '{}' works in this circumstance. It would be my solution but it would also mean that this version of "find" is considered abnormal or that the shell is misbehaving with the braces {}.

Please post what shell you are using. I'd assume it would be bash but it doesn't have to be.

Is the "find" typed at the command prompt or within a script?

The command with '{}' did not work and I am running it from the BASH command line and in a crontab entry.

drew k's command did work however. Thanks drew!

Thank you markdjones82 for your response.
It is clear that this is not a '{}' issue.

I believe that the issue was that directory
/home/chroot/marketing/user
and its subdirectories was deleted before the attempt to delete
/home/chroot/marketing/user/download/Fake Name

The suggestion to use "-depth" was a good idea.

Yeah I tried -depth and it also did not work :confused: What exactly does depth do again?

If the problem was that rm cannot find the file, the error message would begin with "rm:" not "find:". find doesn't invoke a shell to execute the rm command and so there is no field splitting. The issue as I can reproduce here is that find finds the directory, correctly execs the rm which removes the directory, and then when find tries to descend into that directory to look for others it prints that error message because the directory is no longer there. radoulov's suggestion to use -depth fixes that by telling find to descend into directories first before processing the directory itself. In this case, that's inefficient since the directory is going to be removed anyway. A better option would be -prune.

find /home/chroot/marketing/ -mindepth 3 -type d -prune -exec rm -rf {} \;

Regards,
Alister

The -depth parameter to "find" should start from the deepest level and work up.
I can see potential issues with "rm -rf" on a directory name because it deletes that directory too.
Personally I have not issued "rm -rf" in years. There is always a safer and more predictable method where you first produce a list to review.

alister's explanation and solution is very good.

Just curious: does the suggested -exec '{}' for work when you run it directly? ie, not from a crontab entry?

Me too. The issue with "find" and {} or '{}' regarding arguments containing space characters has been evident for many years. Having examined the source code of a current unix "find" there is no reason to quote {}. However in my experence it is sometimes necessary to quote '{}'. This board has experienced posters who could resolve this issue. With current shells it has to be something to do with context.

Definitely.
As far as efficiency is concerned,
one could reduce further the fork/exec storm:

  • use + instead of \; to make it work like xargs (n at a time):
... -exec rm -r {} +

(I believe that -f is not needed in a non interactive sessions, i.e. cron)

or, if the + syntax is not supported:

  • pipe the output to xargs, like already suggested

---------- Post updated at 09:03 AM ---------- Previous update was at 08:56 AM ----------

Yep,
I remember that discussion very well,
that's why I asked for details :slight_smile:

And that's why I suggested the depth option, to see if the filename really matters.

Nope, none of the '{}' options or using the -depth worked, but the xargs did work.

or has been a urban legend for many years, depending on who you ask ... :wink:

You are still missing these quotes are stripped off by the shell before the braces are passed to the find command and so there is no much point in expecting them to have an effect on the find command itself. The only reason to quote them would be to use a shell that has a special interpretation of these curly braces, but there is no such beast around.

There is no such evidence I'm aware of.

Anyway, back to the open poster issue, it would be interesting to get a reproducible use case to figure out why xargs work where find alone doesn't.

I'm especially interested seeing what display these three commands:

 find /path -mindepth 3 -type d -exec /bin/rm -rf {} \;
 find /path -mindepth 3 -type d -exec rm -rf {} +

and

 find /path -mindepth 3 -type d -exec echo "[" {} "}" \;