使用 grep、awk 或 sed 查找与一种模式匹配但不与另一种模式匹配的文件

使用 grep、awk 或 sed 查找与一种模式匹配但不与另一种模式匹配的文件

我需要找到不包含一个字符串(例如)但包含另一个字符串(例如)的.php文件。.plaaabbb

我目前正在使用这个命令:

find /path/ \( -iname '*.php*' -or -name '*.pl*' \) -exec sh -c 'grep -l -v "aaa" {} | grep -l "bbb" {}' \; > resulttofile

大约有 50 万个文件需要搜索,所以我想知道,

  • 如果我的命令正常工作 - 一些眼睛采样会给出积极的结果,
  • 如果可以使用某种其他形式变得更快(目前在虚拟机上大约需要 2 分钟,但将添加更多文件),或者使用awkorsed代替grep- 或者可能只是一种组合grep而不是两种。

该系统是 Debian GNU/Linux。

答案1

您的命令无法正常工作:第一个命令grep将列出包含不匹配行的任何文件"aaa",第二个命令grep将忽略第一个命令的输出,因为它给出了自己的要处理的文件 - 因此您将获得匹配的文件列表"bbb",无论如何它们是否包含"aaa"。您需要要求grep仅在不包含任何行匹配"aaa"( grep -L) 的情况下列出文件,并使用xargs处理结果文件列表并仅将其提供给第二个文件grep(或使第二个grep文件以第一)。

最重要的是,只有find列出的文件名不会给 shell 带来问题,它才会起作用 - 特别是,{}直接包含在给定的命令中sh -c意味着文件名最终会被解释为 shell 命令(请参阅是否可以安全地使用“find -exec sh -c”?了解详情)。

假设您使用的是 GNU ,以下内容将需要更少的grep调用并且更安全:grep

find /path/ \( -iname '*.php*' -o -name '*.pl*' \) -exec grep -LZ aaa {} + |
  xargs -r0 grep -l bbb

-or运算符是 GNU 的扩展find。使用-o为了便携性。

答案2

未经测试,但这应该做我认为你想要的,使用 GNU awk fornextfileENDFILE

find /path/ \( -iname '*.php*' -or -name '*.pl*' \) -exec awk '
    /aaa/{a=1} /bbb/{b=1} a&&b{nextfile} ENDFILE{if (b && !a) print FILENAME; a=b=0}
' {} + > resulttofile

上面一次只对多个文件调用 awk 一次,因此应该是高效的。

上面是如何通常匹配文件中的多个模式,然后在完全读取文件后评估匹配组合的结果,但如下@G-Man 说“恢复莫妮卡”中提到的一条评论在这种特定情况下,您可以通过在匹配时停止读取当前文件来提高效率,aaa因为成功标准是aaa不存在:

/aaa/{a=1; nextfile} /bbb/{b=1} ENDFILE{if (b && !a) print FILENAME; a=b=0}

答案3

您可以将多个 -exec 指令(或其他指令)与一个 find 命令链接在一起:

find /path \( -iname '*.php*' -or -name '*.pl*' \) -exec grep -q "bbb" {} ";" \
     -exec grep -L "aaa" {} ";" > resulttofile

(换行只是为了适应 SE 的布局)。

相关内容