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?
    – graham
    Commented Sep 15, 2019 at 16:04
  • 6
    -mindepth 1 ? Commented Sep 15, 2019 at 16:06
  • @Graham /parentdir/ deletes parentdir too, so makes no difference.
    – TNT
    Commented Sep 15, 2019 at 16:58
  • ah, I see I missed the * at the end which @Amourk mentions in his answer.
    – graham
    Commented 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. Commented 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. Commented 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? Commented 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. Commented 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. Commented 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
    Commented 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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .