如何搜索包含一组单词的文本文件(例如,AAAA & (BBB | CCCCC) & ~DDD)?

如何搜索包含一组单词的文本文件(例如,AAAA & (BBB | CCCCC) & ~DDD)?

我需要找到满足相对复杂条件的文件。例如,我想查找满足以下所有条件的所有文件:

  • 包含单词 AAAA
  • 包含单词 BBB 或 CCCCC(可能两者都包含)
  • 不包含单词 DDD

这些单词可以以任何顺序出现,并且可以出现在不同的行(或同一行)中。

我有一个解决方案,它结合了findegrep,但它不是很清晰。

find . \( -type f -and -exec egrep -q 'BBB|CCCCC' {} \; \
     -and      -exec egrep -q AAAA {} \; \
     -and -not -exec egrep -q DDD {}  \;    \) -print

有没有更好的方法来解决这个问题?

答案1

在我看来,您的解决方案对于该任务来说非常清晰。然而,它很慢,因为它为每个文件生成 3 个进程。我认为 Awk 更适合这里,因为它允许一次读取整批文件(如 ARG_MAX 允许的),使用{} +而不是{} \;.

GNU awk:

find . -type f -exec gawk '
    BEGINFILE{c1=c2=c3=0}
    /AAA/       {c1=1}
    /BBB/||/CCC/{c2=1}
    /DDD/       {c3=1; nextfile}
    ENDFILE{if(c1 && c2 && !c3)print FILENAME}
' {} +

POSIX *

find . -type f -exec awk '
    FNR==1{
        if(NR>1 && c1 && c2 && !c3)print f
        c1=c2=c3=0
        f=FILENAME
    }
    /AAA/       {c1=1}
    /BBB/||/CCC/{c2=1}
    /DDD/       {c3=1; nextfile}
    END{if(c1 && c2 && !c3)print f}
' {} +

*实际上,nextfile仍然不是 POSIX 但是已被下一期标准接受。您可以将其删除以符合 POSIX Issue 7 合规性;结果是一样的,但性能会受到影响。


笔记:如果 awk 没有读取文件的权限,它就会退出。在 GNU Find 中,只需添加该-readable标志即可避免这种情况。如果 GNU Find 不可用,Test 可以用作附加过滤器:

find . -type f -exec test -r {} \; -exec awk '
    ...
' {} +

但是为每个文件生成一个测试会带来性能损失。


进一步阅读:

答案2

这是另一种方法。它的工作原理是首先创建包含每个单词的文件列表:

find . -type f -exec grep -lF 'AAA' {} + > files_with_AAA
find . -type f -exec grep -lF 'BBB' {} + > files_with_BBB
...

然后可以通过处理列表来实现条件:

grep -xFf files_with_BBB files_with_AAA   # AAA & BBB
grep -xFvf files_with_BBB files_with_AAA  # AAA & ~BBB
sort -u files_with_AAA files_with_BBB    # AAA | BBB

如果有大量文件和多个表达式需要计算,则速度会更快,因为不必重新扫描每个文件。

答案3

我有兴趣查看与这些解决方案的时间比较,特别是对于ripgrep,它具有内置的并行处理功能。

  1. GNU grep

    grep -rLZ 'DDD' | xargs -0 grep -lZ 'AAAA' | xargs -0 grep -lE 'BBB|CCCCC'
    
    # if your search terms are literal strings
    grep -rLZF 'DDD' | xargs -0 grep -lZF 'AAAA' | xargs -0 grep -lF -e 'BBB' -e 'CCCCC'
    
  2. rg。请注意,默认情况下启用递归搜索,并且默认情况下也会忽略某些文件。-u如果您不希望此类文件.gitignore 影响结果,则需要使用。用于 -uu额外搜索隐藏文件。-uuu如果您还想搜索二进制文件,请使用 。

    rg --files-without-match -0  'DDD' | xargs -0 rg -l0  'AAAA' | xargs -0 rg -l 'BBB|CCCCC'
    
    # if your search terms are literal strings
    rg --files-without-match -0F 'DDD' | xargs -0 rg -l0F 'AAAA' |
                                         xargs -0 rg -lF -e 'BBB' -e 'CCCCC'
    
  3. 具有rg多行匹配

    rg -lUP '(?s)\A(?!.*DDD)(?=.*(BBB|CCCCC)).*AAAA'
    

相关内容