【问题标题】:Use GNU find to show only the leaf directories使用 GNU find 仅显示叶目录
【发布时间】:2011-05-15 06:19:38
【问题描述】:

我正在尝试使用 GNU find 仅查找不包含其他目录但可能包含也可能不包含常规文件的目录。

到目前为止,我的最佳猜测是:

find dir -type d \( -not -exec ls -dA ';' \)

但这只是让我得到一长串“。”

谢谢!

【问题讨论】:

标签: bash shell find gnu


【解决方案1】:

如果您的文件系统符合 POSIX 标准,则可以使用 -links(即,目录中的每个子目录都有一个链接,一个来自其父级的链接和一个指向自身的链接,因此如果它没有子目录,则计数为 2 个链接)。

下面的命令应该做你想做的事:

find dir -type d -links 2

但是,它似乎不适用于 Mac OS X(正如@Piotr 提到的那样)。这是另一个较慢的版本,但可以在 Mac OS X 上运行。它基于他的版本,并更正了处理目录名称中的空格:

find . -type d -exec sh -c '(ls -p "{}"|grep />/dev/null)||echo "{}"' \;

【讨论】:

  • @SylvainDefresne,知道它是否可以在 NFS 上的 NetApp 文件系统上工作?
  • 我刚刚在 NFS 上的 NetApp 上使用了第一个版本(-links 2)。所以答案是肯定的。
  • 同样,简单的 soln 似乎在 Cygwin (windows 7) 中不起作用,但扩展的 OSx 版本可以
  • 在我的 btrfs 系统目录中的链接计数为 1,所以这不起作用。
  • 替换字符串 {} 应该用单引号引用到 sh -c,而不是双引号,因为文件名可能包含在双引号下特殊处理的字符(例如 $)。
【解决方案2】:

我刚刚找到了另一种适用于 Linux 和 macOS 的解决方案(没有 find -exec)!

涉及sort(两次)和awk

find dir -type d | sort -r | awk 'a!~"^"$0{a=$0;print}' | sort

说明:

  1. find输出进行倒序排序

    • 现在您首先出现子目录,然后是它们的父目录
  2. 如果当前行是前一行的前缀,则使用awk 省略行

    • (此命令来自answer here
    • 现在您消除了“所有父目录”(您只剩下父目录)
  3. sort 他们(所以它看起来像正常的 find 输出)
  4. 瞧!快速便携。

【讨论】:

  • 这个巧妙/便携的答案的唯一问题是,正如here 指出的那样,如果文件夹名称中的任何字符是正则表达式特殊字符,它将失败。我做了一个小修改并发布了我的答案here
  • 如果一个目录以另一个目录的子字符串开头,这将不起作用。例如,如果一个叶子目录名为“foo”,另一个名为“foobar”,则只会显示“foobar”。
  • 就此而言,您可以使用 sed 在 awk 之前将“/”附加到每行的末尾,然后在 awk 之后将其删除
【解决方案3】:

@Sylvian 解决方案在 mac os x 上对我不起作用,原因不明。所以我想出了一个更直接的解决方案。希望这会对某人有所帮助:

find . -type d  -print0 | xargs -0 -IXXX sh -c '(ls -p XXX | grep / >/dev/null) || echo XXX' ;

解释:

  • ls -p 以 '/' 结束目录
  • 所以如果没有目录,(ls -p XXX | grep / >/dev/null) 返回 0
  • -print0 && -0 是让 xargs 处理目录名中的空格

【讨论】:

    【解决方案4】:

    我的目录树中有一些奇怪命名的文件,它们混淆了awk,如 @AhmetAlpBalkan 的回答。所以我采取了稍微不同的方法

      p=;
      while read c;
        do 
          l=${#c};
          f=${p:0:$l};
          if [ "$f" != "$c" ]; then 
            echo $c; 
          fi;
          p=$c; 
        done < <(find . -type d | sort -r) 
    

    awk 解决方案一样,我反向排序。这样,如果目录路径是上一次命中的子路径,您就可以轻松辨别。

    这里p是我之前的匹配,c是当前匹配,l是当前匹配的长度,f是上一个匹配的第一个l匹配字符。我只echo那些不匹配上一场比赛开始的命中。

    所提供的awk 解决方案的问题在于,如果路径名在某些子目录的名称中包含诸如+ 之类的内容,则字符串开头的匹配似乎会混淆。这导致awk 为我返回了一些误报。

    【讨论】:

    • 在处理文件时引用你的变量,否则事情会中断。
    【解决方案5】:

    这个awk/sort 管道比最初提议的in this answer 工作得好一点,但很大程度上基于它:) 无论路径是否包含正则表达式特殊字符,它都会更可靠地工作:

    find . -type d | sort -r | awk 'index(a,$0)!=1{a=$0;print}' | sort
    

    请记住,awk 字符串是 1 索引而不是 0 索引的,如果您习惯于使用基于 C 的语言,这可能会很奇怪。

    如果上一行中当前行的索引是 1(即以它开头),那么我们跳过它,这就像 "^"$0 的匹配一样。

    【讨论】:

    • 这将无法匹配名称为同级目录前缀的目录。例如。如果你有路径/a/a/a/ab,那么/a/a 将不会被报告。
    【解决方案6】:

    这个呢?它是可移植的,并且不依赖于 finnicky 链接计数。但请注意,将root/folder 不带 结尾的 / 放置是很重要的。

    find root/folder -type d | awk '{ if (length($0)<length(prev) || substr($0,1,length(prev))!=prev) print prev; prev=($0 "/") } END { print prev }'
    

    【讨论】:

      【解决方案7】:

      这是适用于 Linux 和 OS X 的解决方案:

      find . -type d -execdir bash -c '[ "$(find {} -mindepth 1 -type d)" ] || echo $PWD/{}' \; 
      

      或:

      find . -type d -execdir sh -c 'test -z "$(find "{}" -mindepth 1 -type d)" && echo $PWD/{}' \;
      

      【讨论】:

      • 不应该是-maxdepth吗?
      猜你喜欢
      • 1970-01-01
      • 2018-02-02
      • 2011-05-12
      • 2012-11-24
      • 1970-01-01
      • 2012-05-04
      • 1970-01-01
      • 2012-11-07
      • 1970-01-01
      相关资源
      最近更新 更多