如何只查找没有子目录的目录?

all*_*ije 8 linux ls find shell-script directory-structure

在 linux 中是否有一种方法可以查看目录树,仅查看那些位于分支末端的目录(我将在此处称它们为叶子),即其中没有子目录的目录?我看了这个问题,但从来没有正确回答。

所以如果我有一个目录树

root/
??? branch1
?   ??? branch11
?   ?   ??? branch111   *
?   ??? branch12        *
??? branch2
    ??? branch21        *
    ??? branch22
        ??? branch221   *
Run Code Online (Sandbox Code Playgroud)

我可以只找到位于其分支末尾的目录(标有 的目录*),所以只查看目录数,而不是文件数?在我的真实案例中,我正在寻找带有文件的文件,但它们是我想在本示例中找到的“叶子”的子集。

Bod*_*odo 12

要仅查找包含文件的叶目录,您可以结合参考问题https://unix.stackexchange.com/a/203991/330217或类似问题/sf/answers/298890371/的答案或https://serverfault.com/a/530328带有find's! -empty

find rootdir -type d -links 2 ! -empty
Run Code Online (Sandbox Code Playgroud)

检查硬链接-links 2应该适用于传统的 UNIX 文件系统。该-empty条件不是 POSIX 标准的一部分,但应该在大多数 Linux 系统上可用。

根据 KamilMaciorowski 的评论,目录的传统链接计数语义对 Btrfs 无效。这在https://linux-btrfs.vger.kernel.narkive.com/oAoDX89D/btrfs-st-nlink-for-directories中得到证实,其中还提到 Mac OS HFS+ 作为传统行为的例外。对于这些文件系统,需要使用不同的方法来检查叶目录。

  • 在 Btrfs 中,任何目录都报告链接数为 1。我猜它不是“普通的 UNIX 文件系统”,然后…… (3认同)

pLu*_*umo 5

您可以使用嵌套find和计算子目录的数量:

find . -type d \
  \( -exec sh -c 'find "$1" -mindepth 1 -maxdepth 1 -type d -print0 | grep -cz "^" >/dev/null 2>&1' _ {} \; -o -print \)
Run Code Online (Sandbox Code Playgroud)


Sté*_*las 5

zsh

leafdirs=( **/*(NDFl2) )
Run Code Online (Sandbox Code Playgroud)

$leafdirs使用匹配的叶目录列表设置数组。

您可以使用以下命令在列上print列出raw 1 C

print -rC1 -- $leafdirs
Run Code Online (Sandbox Code Playgroud)

或者使用以下命令循环它们:

for dir ($leafdirs) something with $dir
Run Code Online (Sandbox Code Playgroud)

这或多或少是@Bodo 的 GNUfind答案的翻译,所以它只适用于传统的类 Unix 文件系统,比如ext4该实现...实际的目录条目。

  • **/:任何级别的子目录
  • (NDFl2): 全局限定符
  • N:启用nullglob该 glob:如果没有匹配,则不会失败。相反,结果是一个空列表
  • D:启用dotglob该 glob:查看隐藏目录,并且不要跳过隐藏文件
  • F:选择完整目录:目录至少包含一个除 和 以外的条目...如 GNUfind-type d ! -empty
  • l2:仅链接计数为 2 的目录(如-links 2)。一种用于其父目录中的目录条目,另一种用于.其中的目录条目。..由于其中的条目,任何子目录都会为链接计数加一。

在无法使用 nlinks == 2 的文件系统上,您可以执行以下操作:

leafdirs=( **/*(NDF^e['()(($#)) $REPLY/*(ND/Y1)']) )
Run Code Online (Sandbox Code Playgroud)

我们使用e限定符(用 否定^)运行代码来检查目录(存储在该代码中)是否有子目录,这里使用匿名函数来检查中的目录$REPLY的扩展是否在第一个匹配处停止,传递的参数是一个非空列表(非零)。$REPLY/*(ND/Y1)$REPLY$#