Linux查找-type d:如何列出最深的目录,例如没有./dir1的./dir1/dir2?

Linux查找-type d:如何列出最深的目录,例如没有./dir1的./dir1/dir2?
find . -type d

一般会显示

./dir1
./dir1/dir2
./dir3
./dir3/dir4
./dir3/dir4/dir5
...

我只想

./dir1/dir2
./dir3/dir4/dir5

,没有其父级 ./dir1,...

换句话说,我只想查看其中没有任何子目录的目录。

任何想法?

编辑:我发现它-links 2可以在正常的Linux环境中工作,例如,

docker run -it --rm ubuntu:bionic find /etc -type d -links 2

工作完美。

但是,当我将目录(从 MacOS 或 Windows)安装到 docker 容器中时,情况发生了变化,它不起作用,您可以尝试以下操作:

docker run -it --rm -v /etc:/etc_of_host ubuntu:bionic find /etc_of_host -type d -links 2

答案1

zsh

has_subdirs() () (( $# )) ${1-$REPLY}/*(ND/Y1)
print -rC1 -- **/*(ND/^+has_subdirs)

或者直接不带中介函数:

print -rC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])

那是:

  • fn() some-command定义一个fn函数作为some-command主体,就像 Bourne-shell 和大多数类似 Bourne 的 shell 中一样。
  • () { code } args作为位置参数运行code的匿名函数。args
  • (( $# ))这里匿名函数的主体是一个算术表达式,它解析为真的if $#(参数数量)非零。
  • ${param-default}(来自 Bourne shell)扩展为$param是否设置了参数或default其他情况。在这里${1-$REPLY},我们允许直接调用我们的函数(如has_subdirs mydir)或作为如下所示的全局限定符函数。
  • $dir/*(ND/Y1)$dir扩展到( )中目录类型的文件,/包括隐藏文件 ( Dotglob ),并且如果没有匹配 ( Nullglob ),也不会出错。但是对于Y1,我们停在第一场比赛。因此has_subdirs,如果目录至少包含一个子目录,则匿名函数(因此)将返回 true。
  • print -rC1在olumn上打印其参数raw1 C
  • ^+has_subdirs仅限于该has_subdirs函数未 ( ^) 返回 true 的文件。

如果必须使用bashshell,只需执行以下操作:

zsh << 'EOF'
  print -rC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
EOF

或者将结果存储在 bash 数组中(需要 bash-4.4+):

readarray -td '' array < <(zsh << 'EOF'
  print -rNC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
EOF
)

(使用 NUL 分隔的记录以便能够存储任意路径)。

如果你没有zsh,但你有perl,你可以这样做:

find . -depth -type d -print0 |
  perl -l -0ne 'print if rindex $prev, "$_/", 0; $prev = $_'

或者如果你有 GNU awk

find . -depth -type d -print0 |
  gawk -v 'RS=\0' 'index(prev, $0"/") != 1; {prev = $0}'

那些已经find打印了目录深度优先(在它们所在的分支之前离开)并获取perl/awk打印未找到的记录,然后/在上一条记录的开头。

同样,要将文件存储在 bash 4.4+ 数组中,您需要通过移动 after-l或add in 来-0在输出上切换到 NUL 分隔记录:perl-v 'ORS=\0'gawk

readarray -td array < <(
  find . -depth -type d -print0 |
    perl -0lne 'print if rindex $prev, "$_/", 0; $prev = $_'
)
readarray -td array < <(
  find . -depth -type d -print0 |
    gawk -v 'RS=\0' -v 'ORS=\0' 'index(prev, $0"/") != 1; {prev = $0}'
)

在某些系统和文件系统(例如ext4基于 Linux 的系统上的文件系统)上,您可能可以依赖这样一个事实:具有子目录的目录的链接计数大于 2(dirdir/.以及每个 的附加链接dir/subdir/..)。

print -rC1 -- **/*(ND/l-3)

find或者在任何 shell 中使用:

find . -type d -links -3

这在目录的链接计数始终为 1 的情况下不起作用btrfs(在多个 Linux 发行版中已被替换为默认文件系统),因此我不建议将其作为通用解决方案。ext4

联合文件系统与您的情况相同,例如overlayfs我找到一些合并目录的链接计数为 1,无论它们是否包含子目录。

但是,无论在哪里可用,它都比手动计算子目录的解决方案具有优势,即您不需要对目录进行读取访问(只需对其父目录进行搜索访问)。

答案2

您要寻找的是:

find . -type d -links 2

文件系统中的每个目录至少有两个硬链接 - 父目录中的目录本身和“.”入口。子目录中的每个“..”条目都会向目录添加一个新的硬链接。所以你只需要找到有两个硬链接的目录即可。

另一个答案中建议的命令

find . -type d -links -3

执行相同的操作,但声明“少于三个硬链接的目录”。

答案3

最排序:

p="";(find . -type d;echo) | while read d; do [[ $p && $d != $p/* ]] && echo $p; p=$d; done

在一行中

a="";(find . -type d;echo )|while read i; do if  [[ (! $i =~ $a) && ("$a" != "") ]]; then echo $a; fi; a=$i;done

正如op提到的:

a="";(find . -type d;echo )|while read i; do [[ $i != $a/* && ! -z "$a" ]] && echo $a; a=$i;done

答案4

man find。该-mindepth选项就是您想要的。

相关内容