15

When I use

find /home/user/parentdir -type d -empty -delete

it looks recursively for empty subfolders inside /home/user/parentdir and deletes them. But if /home/user/parentdir is also empty, it deletes the parentdir folder too, which is undesirable for me.

I want to keep this parentdir to rsync some files to backup or cloud. After processing, I need to delete the empty folders, but seems unproductive to recreate parentdir every time.

Any suggestions to keep parentdir? I thought about creating a .nocopy file inside it and exclude it from rsync, but looks like overkill. Is there a more elegant way?

4
  • if you add a forward slash / to the end of /parentdir (ie /parentdir/)does that make a difference?
    – 24601
    Sep 15, 2019 at 16:04
  • 6
    -mindepth 1 ? Sep 15, 2019 at 16:06
  • @Graham /parentdir/ deletes parentdir too, so makes no difference.
    – TNT
    Sep 15, 2019 at 16:58
  • ah, I see I missed the * at the end which @Amourk mentions in his answer.
    – 24601
    Sep 15, 2019 at 17:14

4 Answers 4

24

Simply do find /home/user/parentdir -mindepth 1 -type d -empty -delete.

Look:

$ mkdir -p test1/test2
$ find test1 -type d
test1
test1/test2
$ find test1 -mindepth 1  -type d
test1/test2

The find /home/user/parentdir/* in AmourK’s answer is undesirable when there are a lot of files and it is overcomplicated.

0
15

By adding /* to the end of parentdir, you are performing the action on all subdirs of parentdir rather than on parentdir itself. And so in the same way /home/user/ is not deleted in the old command, parentdir will not be not be deleted in the command below. * is called a glob operator and it matches any string of characters.

find /home/user/parentdir/* -type d -empty -delete

5
  • 7
    One thing to be aware of with this approach is if there is a large number of files in /home/user/parentdir/, the expanded glob may exceed ARG_MAX, resulting in an argument list too long error. You could reduce the chance of that happening by changing the glob to */ so that it matches directories only. Sep 15, 2019 at 20:56
  • 12
    Also beware that this will not find any children starting with a dot. And someday you will realize this, and if you also do a find for ".*" you will be in a huge world of hurt (because ".*" matches ".."). Ask me how I know. Sep 16, 2019 at 0:56
  • 1
    @GlennWillen why do I think we should be sitting at a bar with drinks in front of us before I ask you to tell this story? Sep 16, 2019 at 22:06
  • @MontyHarder It fortunately wasn't as bad as it could have been. It was on my first Unix system, which was a Solaris machine owned by my high school. Thank god I wasn't running 'rm', but rather 'chown'. I was using root to try to fix ownership of everything in my home directory. Instead I gave myself ownership of EVERY home directory (and all their contents) and then had to put them all back. Sep 16, 2019 at 23:59
  • I had a co-worker training a new employee on how to install a piece of software. It involved going to /tmp, creating a subdirectory, unpacking a gzipped tarball there, running the installer, and cleaning up. The newbie thought rm -rf /tmp/foo-install was the right way to do that, and as the experienced hand said "Noooooo" in his best Darth-Vader-at-the-end-of-Episode-III impression, the newb found out the hard way how close the / and Enter keys were to each other. We had to boot from the backup software's restore disk and reload the whole server from tape. Sep 17, 2019 at 17:13
0

if you have php-cli installed,

printf %s $(pwd) | php -r 'function f(string $dir){var_dump($dir);$dir.=DIRECTORY_SEPARATOR;foreach(glob("$dir*",GLOB_ONLYDIR) as $d){f($d);}global $original;if(substr($dir,0,-strlen(DIRECTORY_SEPARATOR))!==$original && empty(glob("$dir*"))){rmdir($dir);}}f(($original=stream_get_contents(STDIN)));'
1
  • you wanted foreach (RecursiveIteratorIterator(RecursiveDirectoryIterator($dir)) as $p) if (empty($skipped)) $skipped = TRUE; elseif ($p->isDir()) @rmdir($p->getPathname()); or something close to that. $skipped takes care of skipping the first one, @rmdir attempts to remove a dir and silently (that's the @) fails if not empty.
    – chx
    Sep 16, 2019 at 9:06
0

When you only need one level (leaving a parent folder, but deleting empty child folders), an easy trick is to use rmdir.

rmdir parent/*

Deletes all empty subfolders, but only prints an error for files and non-empty folders.

This does not work recursively, but when knowing your folder structure it may be the fastest way to do something like

rmdir parent/*/*/* # deletes parent/a/b/c when empty
rmdir parent/*/* # deletes parent/a/b when it is now empty
rmdir parent/* # deletes parent/a when it is now empty

Advantage: Easy to remember, fast to type, little potential for choosing a wrong parameter.
Disadvantage: Less flexible, possibly a lot of error messages in your terminal.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.