通过管道查找到另一个查找不起作用

通过管道查找到另一个查找不起作用

这是我的用例:

搜索整个机器,找到任何名为 X 的目录

当然,这条线有效:

find / -type d -name "X"

但是,它有点慢,而且我认为它使用了大量资源。

为了提高速度,我考虑将查找结果传输到其他目录中,以过滤可能的搜索结果。例如,只查找根目录中首字母大写的目录,然后在其中查找名为X

find / -maxdepth 1 -type d -name "/[A-Z]*" | xargs find -type d -name "X"

然而,这并没有奏效。

我已经看过了如何将 find 的输出通过管道传输到另一个 find 中但我找不到一些发现的管道说明。

如何将查找内容通过管道传输到另一个查找内容?

答案1

find在 another 的结果上运行find,但不使用该语法。

find / -maxdepth 1 -type d -name "/[A-Z]*" | xargs find -type d -name "X"

首先,您不能在任何命令或其他命令xargs的输出上使用类似的方法,除非它输出的文件名都不包含任何空白字符、引号或反斜杠(或某些实现中的非字符)。findfind

如果您希望能够处理任意文件,则只能在输出(也是非标准)上使用xargs非标准选项。其本身的输出根本无法进行后处理(请参阅-0find -print0-print0find -print为什么循环查找的输出是不好的做法?)。

另外,在这里,xargs附加第二个命令的文件路径find,并将它们放在构成过滤条件的谓词之后。find必须给出要操作的文件列表任何谓词。

更一般地,很少需要xargs在 的输出上使用,因为它有自己的内置(并且更可靠且通常更高效)支持在其使用/ (以及某些/ )谓词找到的文件上运行命令。findfind-exec-ok-execdir-okdir

但与 一样xargs,您需要确保第二个文件列表find位于谓词之前,因此它必须是:

find / -maxdepth 1 -name '[[:upper:]]*' -type d -exec sh -c '
   exec find "$@" -name X -type d' sh {} +

我们使用like-exec cmd {} +的形式传递尽可能多的可能路径,但也只能在最后传递它们。用于将那些移动到第二个正确的位置。-execxargscmdshfind

另请注意,-name匹配的是文件名,而不是其完整路径(您需要-path)。因此,我们[[:upper:]]*不需要/[[:upper:]]*(也不需要[A-Z]*根据区域设置匹配的内容通常相当随机)匹配以大写字母开头的文件名。

使用 GNU 的下一个版本find(或其当前的开发版本),您还可以执行以下操作:

find / -maxdepth 1 -name '[[:upper:]]*' -type d -print0 |
  find -files0-from - -name X -type d

在这里,您可以通过一次调用来完成整个过程find

find / -path '/[![:upper:]]*' -prune -o -name X -type d -print

我们告诉在查找名为 X 的目录之前find修剪以名称以大写字母以外的任何字符开头的目录开头的树分支。/

请注意,对于某些系统(包括GNU 系统上的 GNU)find上的某些实现,可能无法匹配文件名中当前语言环境中无效文本的部分。find*

例如,上面的命令会发现/stéphane/X即使s不是大写字母,如果它é是用 iso8859-1 编码的,并且当前语言环境使用 UTF-8 作为其字符映射(其中 0xe9 字节无法解码为字符,因此*无法不匹配),并且第一个命令将/Stéphane/X因同样的原因而无法找到。

zshglob 不存在此类问题,因为将无法解码为字符的每个字节视为未定义的字符,因此您可以这样做:

print -rC1 /[[:upper:]]*/**/X(ND/)

或者,如果您不需要对列表进行o排序,则可以进行轻微的优化:

print -rC1 /[[:upper:]]*/**/X(ND/oN)

请注意,它将包括/SymLink/.../X目录。为了避免这种情况:

(){print -rC1 $^@/**/X(ND/oN)} /[[:upper:]]*(N/oN)

或者:

print -rC1 /[[:upper:]]*(N/oNe['reply=($REPLY/**/X(ND/oN)'])

这类似于两阶段find方法:在一个 glob 中查找名称以大写字母开头的目录,然后将其中的所有 X 目录作为单独的 glob。

答案2

您的任务是找到/首字母大写的子目录,然后提取这些子目录的路径名X

对于find,我会这样做,假设没有数千个/首字母大写的目录,

find /[[:upper:]]*/X -prune -type d -print 

上面,我们find使用一组顶级搜索路径进行调用。这些路径名恰好对应于我们实际要查找的目录名。唯一的任务find是调查其中的每一个并打印目录。

您也可以find完全跳过,而只是使用

printf '%s\n' /[[:upper:]]*/X/

这里唯一的区别是该模式可能与目录的符号链接匹配。如果这对您很重要,您可以对匹配名称进行更明确的测试,并告诉 shell(我假设是bash)也匹配隐藏名称:

shopt -s nullglob

for name in /[[:upper:]]*/X/; do
    [ -L "$name" ] && continue
    printf '%s\n' "$name"
done

反复阅读您的问题几次后,我意识到我不知道您是否想要搜索X 递归的或不。在上面的讨论中,我假设你这样做不是想要递归搜索。

如果你想要递归搜索,那么你会使用

find /[[:upper:]]* -name X -type d -print

没有什么可以使这个速度更快,除非您将搜索限制为更少的顶级搜索路径,或者修剪您所使用的已知路径。知道您不想搜索,例如避免输入任何tmp目录,

find /[[:upper:]]* -name X -type d -print -o -name tmp -prune

您可能希望将搜索限制为单个文件系统:

find /[[:upper:]]* -xdev -name X -type d -print -o -name tmp -prune

在这里,来自顶级搜索路径的搜索永远不会跨越文件系统边界。

相关内容