단어 집합(예: AAAA & (BBB | CCCCC) & ~DDD)이 포함된 텍스트 파일을 검색하려면 어떻게 해야 합니까?

단어 집합(예: AAAA & (BBB | CCCCC) & ~DDD)이 포함된 텍스트 파일을 검색하려면 어떻게 해야 합니까?

비교적 복잡한 기준을 충족하는 파일을 찾아야 합니다. 예를 들어 다음 기준을 모두 충족하는 모든 파일을 찾고 싶습니다.

  • AAAA라는 단어가 포함되어 있습니다.
  • BBB 또는 CCCCC(둘 다 가능)라는 단어가 포함되어 있습니다.
  • DDD라는 단어가 포함되어 있지 않습니다.

단어는 순서에 상관없이 다른 줄(또는 같은 줄)에 나타날 수 있습니다.

find결합된 솔루션 이 있지만 egrep명확하지 않습니다.

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

특히 이러한 솔루션과의 시간 비교를 보고 싶습니다.립그렙, 병렬 처리 기능이 내장되어 있습니다.

  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'
    

관련 정보