如果找到文件则修剪搜索树

如果找到文件则修剪搜索树

我需要在相当大的目录层次结构中搜索名称与特定文件名通配模式匹配的常规文件。层次结构是如此之大(非常深并且有一些巨大的目录),以至于采取简单的方法需要太长时间:

find /top/dir -type f -name 'pattern'

(哪里pattern有类似的模式*proj*.tgz。)

由于目录结构的性质,我知道如果find在目录中找到文件,我可以引入优化来修剪搜索树。例如,在特定目录中查找一个或多个文件意味着我不需要检查该特定目录的任何子目录中的其他匹配项。

由于应用于-prune常规文件并不正确,所以我不能这样做

find /top/dir -type f -name 'pattern' -prune

问题:如何避免搜索包含与模式匹配的文件的目录的子目录?

答案1

人们可以为每个目录调用一个内联脚本。该脚本将检查该模式是否与目录中的任何常规文件匹配。如果模式匹配,则输出(一般情况下,流程而不仅仅是打印)匹配的路径名并从搜索树中修剪父目录:

find /top/dir -type d -exec zsh -c '
    set -- "$1"/pattern(.N)
    [[ $# -eq 0 ]] && exit 1
    printf "%s\n" "$@"' zsh {} \; -prune

我使用zsh内联脚本的 shell 来访问该 shell 的全局限定符。这里使用的限定符(.N),确保只有常规文件与该模式匹配,如果没有匹配的文件,则删除该模式。


用于bash内联脚本:

find /top/dir -type d -exec bash -O nullglob -c '
    unset -v found
    for pathname in "$1"/pattern; do
        if [[ -f "$pathname" ]] && [[ ! -h "$pathname" ]]; then
            printf "%s\n" "$pathname"
            found=true
        fi
    done
    "${found-false}"' bash {} \; -prune

也就是说,让内联脚本循环遍历特定目录中与模式匹配的名称,如果任何名称对应于常规文件,则对其进行处理并设置“标志”。如果该标志设置在末尾,则修剪父目录。

答案2

遍历目录层次结构,如果找到标志文件 ( ),则在每个目录中修剪树pattern,否则搜索所需文件 ( *proj*.tgz)

find /top/dir -type d -exec sh -c 'z=$(find "$@" -maxdepth 1 -type f -name "pattern" -print -quit); [ -n "$z" ]' _ {} \; -prune -o -type f -name '*proj*.tgz' -print

我最终写了一个更复杂的版本,让我能够看到发生了什么。显然,我必须更改/top/dirpattern、 和*proj*.tgz来获取与本地相关的项目。)我会将其包含在此处以供后代使用

find /top/dir -type d \
    -exec bash -c '
        echo "Considering $*";
        z=$(find "$@" -maxdepth 1 -type f -printf "| %p\n" -name "pattern" -printf "Found flag file\n" -quit);
        [[ -n "$z" ]] && echo "$z";
        [[ "$z" =~ "Found flag file" ]] || { echo "No flag found"; exit 1; }
    ' _ {} \; \
    -printf "Pruning tree\n" -prune \
    -o \
    -type f -name '*proj*.tgz' -print

真正的解决方案需要非 POSIX find -maxdepth。调试版本还需要非 POSIX find -printf。有一个替代方法用于实现-maxdepth满足 POSIX 的要求,但我在这里没有使用它;代码本身就足够不透明了。

相关内容