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

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

linux中有没有一种方法可以在目录树中仅查找那些作为分支末端的目录(我在这里将它们称为叶子),即其中没有子目录的目录?我在看这个问题但从未得到正确的答复。

所以如果我有一个目录树

root/
├── branch1
│   ├── branch11
│   │   └── branch111   *
│   └── branch12        *
└── branch2
    ├── branch21        *
    └── branch22
        └── branch221   *

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

答案1

要仅查找包含非目录文件的叶目录,您可以结合引用问题的答案https://unix.stackexchange.com/a/203991/330217或类似问题https://stackoverflow.com/a/4269862/10622916或者https://serverfault.com/a/530328find! -empty

find rootdir -type d -links 2 ! -empty

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

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

答案2

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

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

答案3

zsh

leafdirs=( **/*(NDFl2) )

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

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

print -rC1 -- $leafdirs

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

for dir ($leafdirs) something with $dir

这或多或少是翻译@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)']) )

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

答案4

如果*/文件名通配模式扩展为不是目录名称的内容,则当前目录没有(非隐藏)子目录。

find

find root -type d -exec sh -c 'set -- "$1"/*/; [ ! -d "$1" ]' sh {} \; ! -empty -print

请注意,这会将叶目录中的目录的符号链接视为目录,因为该模式将遍历符号链接。

-empty谓词不是标准的,但经常被实现。如果没有它,您将执行与检测子目录类似的操作:

find root -type d \
    -exec sh -c 'set -- "$1"/*/; [ ! -d "$1" ]' sh {} \; \
    -exec sh -c 'set -- "$1"/*;  [   -e "$1" ]' sh {} \; -print

或者,更有效一点,

find root -type d -exec sh -c '
    dir=$1
    set -- "$dir"/*/
    [ -d "$1" ] && exit 1
    set -- "$dir"/*
    [ -e "$1" ]' sh {} \; -print

或者,采用-links我已经忘记的谓词(谢谢博多):

find root -type d \
    -links 2 \
    -exec sh -c 'set -- "$1"/*; [ -e "$1" ]' sh {} \; -print

相关内容