假设这个文件系统结构:
ROOT
DIR1A
FILE
DIR2A
DIR2B
DIR3A
DIR1B
DIR2C
DIR2D
DIR3B
DIR1C
DIR2E
FILE
从任意目录开始,我如何才能仅列出其最浅的子目录,而这些子目录又包含a)什么都没有或b)一直向下仅包含空目录,但不列出所述空子目录?
也就是说,在上面的情况下,如果我从 ROOT 开始:
- DIR1A 不会列出,因为它包含一个文件。
- DIR2A 将被列出,因为它不包含任何内容。
- DIR2B 将被列出,因为它只包含空目录。
- DIR3A 不会被列出,因为它位于已列出的较浅目录中。
- DIR1B 将被列出,因为它只包含空目录。
- DIR1B 的子目录不会被列出,因为它们位于已列出的较浅目录中。
- DIR1C 和 DIR2E 都不会列出,因为其中嵌套了一个文件。
我相信有一种更有效的方式来表达这一点。也许“我只想列出最高顺序的目录,其中不包含任何内容或仅包含空目录,一直向下”?
编辑:我试图澄清上面的一些语言。
答案1
为了避免遍历目录树太多次并最大限度地减少运行的命令数量,您可以这样做(假设 GNUfind
和sort
类似awk
的 GNU 支持 NUL 作为R
ecordS
分隔符):
find . -type d -print0 -o -printf 'f/%h\0' |
LC_ALL=C sort -zru |
LC_ALL=C awk -F/ -vRS='\0' '
function parent(path) {
sub("/[^/]*$", "", path)
return path
}
$1 == "f" {
sep = path = ""
for (i = 2; i <= NF; i++) {
black[path = path sep $i]
sep = FS
}
next
}
! ($0 in black) && ($0 == "." || parent($0) in black)'
我们将所有下面包含非目录文件的目录涂成黑色,然后打印具有黑色父级(或特殊情况下没有父级.
)的非黑色目录。
请注意,如果目标是删除这些目录,您可以这样做:
find . -depth -type d -empty -delete
-delete
意味着-depth
,但为了清楚起见,我仍然将其添加到此处(正如 GNUfind
手册所建议的那样)。-delete
无论如何,只会删除空目录,这样-empty
我们就可以避免在无法删除非空目录时出现错误消息。通过深度优先,我们最终会删除不包含非d
目录文件的整个结构,删除叶子所在的分支之前的叶子。
-delete
和是来自 BSD 和GNU的-empty
非标准扩展,但现在两者都相当常见。如果你没有它们,你总是可以将两者替换为(并且可能会用 丢弃错误消息,尽管你会错过-delete
-empty
find
find
-exec rmdir {} +
2> /dev/null
全部find
和)的错误消息rmdir
。
答案2
迟来的,给你:
find -type d -exec sh -c '[ -z "$(find "$@" -type f -print -quit)" ]' _ {} \; -print -prune
例子
# Setup your configuration
mkdir -p root/{dir1a/{dir2a,dir2b/dir3b},dir1b/{dir2c,dir2d/dir3b},dir1c/dir2e}
touch root/{dir1a,dir1c/dir2e}/file
# Run the finder
find root -type d -exec sh -c '[ -z "$(find "$@" -type f -print -quit)" ]' _ {} \; -print -prune
# Output
root/dir1b
root/dir1a/dir2b
root/dir1a/dir2a
解释
依次为每个目录调用子shell exec
,从顶层开始向下(即宽度优先)。它从当前点开始搜索文件,true
如果没有则返回。 mainfind
从它的 中获取状态结果exec
,如果成功则打印当前目录并停止搜索该子树的其余部分。