如果目录的任何父目录包含特定文件,如何从查找结果中排除目录

如果目录的任何父目录包含特定文件,如何从查找结果中排除目录

我尝试使用 find 命令递归搜索目录树并返回与给定名称匹配的目录的所有结果。

find . -type d -name '<NAME>'

我得到返回的路径列表,这些路径被打印到日志文件中。我希望能够忽略父目录中包含名为 omit.txt 的文件的任何结果。

./parent_directory/ 名称>

我一直在尝试用以下方法做到这一点

find . -type d -name '<NAME>' \( ! -wholename "*omit.txt*" -prune \)

我正在尝试使用 prune 命令来忽略包含该文件的结果,但我认为这可能只适用于目录。任何帮助将不胜感激。我试图仅用一个命令来完成此操作并尽可能维护输出文件 -

答案1

我认为您不能在 内完全执行此操作find,因为您需要在处理目录时能够看到目录内的文件。-prune必须对目录本身进行操作,但-path其他条件只能看到find已经下降到目录后的文件名。

我能想到的就是分出一个 shell 来查看目录内部。像这样的东西:

$ mkdir -p x y/z ; touch x/foo y/omit.txt
$ find -type d \( -exec sh -c '[ -e "$1/omit.txt" ]' sh {} \; -prune -o -print \)
.
./x

foo -o bar不评估bar是否foo成功,因此如果目录被修剪,则打印不会运行。添加-o -print到末尾也可以打印目录内容。我们不能exec ... {} +在这里使用,因为我们需要它单独作为每个目录的条件。

答案2

如果 busybox 在您的系统上可用,您可以执行以下操作:

busybox find . -type d '(' -exec [ -e '{}/omit.txt' ] ';' -prune \
  -o -name '<NAME>' -print ')'

busybox的find,就像 GNU 的一样,确实扩展{}到文件的路径,即使它只是部分的争论避免了不得不诉诸sh,但更重要的是,在最近的版本中,它能够[在没有 fork() 或 execve() 的情况下运行小程序,因此它比大多数其他find实现效率高几个数量级。

如果名为的目录<NAME>本身可能包含 aomit.txt并且您想在这种情况下打印它,即使仍然被修剪,您可以将其更改为:

busybox find . -type d '(' -exec [ ! -e '{}/omit.txt' ] ';' -o -prune ')' \
  -name '<NAME>'

在 bosh (不是 bash)shell 中,find(like [) 是内置的,并且能够运行一些 shell 代码,而无需使用其-call谓词分叉,这也将节省 fork() 和 execve() :

find . -type d '(' -call '[ -e "$1/omit.txt" ]' {} ';' -prune \
  -o -name '<NAME>' -print ')'

更便携的替代方案是使用find'sFile::Find模块。

perl -MFile::Find -le '
  find sub {
    if (-e "$_/omit.txt") {
      $File::Find::prune = 1;
    } else {
      print $File::Find::name if ($_ eq "<NAME>") && -d;
    }
  }, @ARGV' -- .

答案3

@ilkkachu 对我困扰了一段时间的问题给出了很棒的答案!一定要给他们投赞成票。我只是想添加一些我使用过的变体,这可能会让其他人受益:)

示例输出基于以下结构

./
├── a/
│   ├── b/
│   │   └── world.txt
│   └── hello.txt
├── foo.txt
└── some_git_repo/
    ├── .git/
    ├── README
    └── c/
        └── some_git_submodule/
            ├── .git/
            └── README

打印除 git repos 之外的所有目录

find . -type d \( -exec sh -c 'test -e "$1/.git"' sh {} \; -prune -o -print \)
.
./a
./a/b

1.打印所有目录,包括git repos

2. 不要递归到任何 git 存储库

find . -type d \( -exec sh -c 'test -e "$1/.git"' sh {} \; -print -prune -o -print \)
.
./a
./a/b
./some_git_repo

打印所有文件,除了 git 存储库中的文件

find . \( -exec sh -c 'test -e "$1/.git"' sh {} \; -prune -o -type f -print \)
./foo.txt
./a/b/world.txt
./a/hello.txt

1. 打印所有文件,除了 git repos 中的文件

2.打印排除的git目录

find . \( -exec sh -c 'test -e "$1/.git"' sh {} \; -print -prune -o -type f -print \)
./foo.txt
./a/b/world.txt
./a/hello.txt
./some_git_repo

然后相反...

打印所有 git 存储库

find . -type d \( -exec sh -c 'test -e "$1/.git"' sh {} \; -print -prune \)
./some_git_repo

打印所有 git 存储库,包括子模块

find . -type d \( -exec sh -c 'test -e "$1/.git"' sh {} \; -print \) -o \( -name '.git' -prune \)
./some_git_repo
./some_git_repo/c/some_git_submodule

相关内容