删除空的子文件夹,保留父文件夹。

当我使用时
find /home/user/parentdir -type d -empty -delete

它递归地查找/home/user/parentdir内的空子文件夹并删除它们。但是如果/home/user/parentdir也为空,它会同时删除parentdir文件夹,这对我来说是不可取的。

我想保留这个parentdir用于备份或云同步一些文件。处理完后,我需要删除空文件夹,但每次重新创建parentdir似乎效率低下。

有没有保留parentdir的建议?我考虑在其中创建一个.nocopy文件并将其从rsync中排除,但这似乎有点过度。有更优雅的方法吗?


如果你在/parentdir的末尾添加一个斜杠/(即/parentdir/),这会有什么区别吗? - graham
6-mindepth 1 ? - steeldriver
@Graham /parentdir/也会删除parentdir,所以没有区别。 - TNT
啊,我明白了,我漏掉了末尾的*,正如@Amourk在他的回答中提到的。 - graham
4个回答

只需执行find /home/user/parentdir -mindepth 1 -type d -empty -delete
看:
$ mkdir -p test1/test2
$ find test1 -type d
test1
test1/test2
$ find test1 -mindepth 1  -type d
test1/test2

在有很多文件和过于复杂的情况下,find /home/user/parentdir/* 在AmourK的回答中是不可取的。

通过在`parentdir`的末尾添加`/*`,您将对`parentdir`的所有子目录执行操作,而不是对`parentdir`本身执行操作。因此,在旧命令中,`/home/user/`没有被删除,下面的命令中`parentdir`也不会被删除。 `*`被称为通配符操作符,它匹配任意字符串。
`find /home/user/parentdir/* -type d -empty -delete`

7使用这种方法需要注意的一点是,如果/home/user/parentdir/目录下有大量文件,扩展的通配符可能会超过ARG_MAX,导致出现“参数列表太长”的错误。为了减少这种情况发生的机会,您可以将通配符更改为*/,这样它只匹配目录。 - steeldriver
12请注意,这个方法不能找到以点开头的文件夹。总有一天你会意识到这一点,如果你同时使用" .* "查找,那么你会遇到很大的困扰(因为" .* "匹配".. ")。问我怎么知道的。 - Glenn Willen
1@GlennWillen 为什么我觉得在我请你讲这个故事之前,我们应该坐在酒吧里,面前放着饮料呢? - Monty Harder
@MontyHarder 幸运的是,情况没有变得像可能那样糟糕。当时我在我的第一台Unix系统上操作,那是我高中拥有的一台Solaris机器。谢天谢地,我没有运行 'rm' 命令,而是运行了 'chown' 命令。我使用 root 用户尝试修复我家目录下所有文件的所有权。结果却把每个家目录(以及它们的所有内容)都变成了我的所有权,然后又不得不将它们全部恢复原状。 - Glenn Willen
我有一个同事在教新员工如何安装软件。这个过程涉及到进入/tmp目录,创建一个子目录,在那里解压缩一个gzipped tarball,运行安装程序,然后清理工作。新手错误地认为rm -rf /tmp/foo-install是正确的操作方式,而经验丰富的同事则以他最像第三集结尾时达斯·维德的声音说道:“不要!”可惜新手亲身体会到了/和Enter键有多么接近。我们不得不从备份软件的恢复盘启动,并从磁带重新加载整个服务器。 - Monty Harder

如果您已经安装了php-cli,
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)));'

你想要的是 foreach (RecursiveIteratorIterator(RecursiveDirectoryIterator($dir)) as $p) if (empty($skipped)) $skipped = TRUE; elseif ($p->isDir()) @rmdir($p->getPathname()); 或者类似的代码。$skipped 变量用于跳过第一个,@rmdir 尝试删除目录并在不为空时静默失败(使用了 @ 符号)。 - chx

当你只需要删除一个层级(保留父文件夹,但删除空的子文件夹)时,一个简单的技巧是使用 rmdir 命令。
rmdir parent/*

删除所有空的子文件夹,但只对文件和非空文件夹打印错误信息。
这不会递归地工作,但是当你了解你的文件夹结构时,这可能是最快的方法来执行类似的操作。
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

优点:易于记忆,打字速度快,选择错误参数的可能性小。 缺点:灵活性较差,可能会在终端上出现许多错误信息。