파일에 나타나는 세 단어를 반복적으로 찾아보세요.

파일에 나타나는 세 단어를 반복적으로 찾아보세요.

내 이메일 백업에서 중요한 이메일을 검색하려고 합니다. 수천 개의 파일이 포함된 하위 디렉터리가 있는 디렉터리입니다 .eml(Linux 파일 시스템의 경우). .eml세 단어가 포함된 텍스트 파일을 검색하고 한 단어를 제외하고 싶습니다 .

먼저 한 단어를 검색한 다음 파이프를 통해 다른 단어를 검색해 봅니다.

grep -R 'foo' ~/Directory/path | grep 'bar'

이는 같은 줄에 두 단어가 포함된 파일만 반환하기 때문에 작동하지 않습니다. 전체 파일에 두 단어가 포함된 파일이 필요합니다.

나는 단어가 포함된 파일을 찾고 파일 내용을 출력 파일로 연결하려고 합니다.

grep -rIlZ  '.' -e 'foo' | xargs -0 cat > MyOutputFile 

맥락을 볼 수 있기 때문에 도움이 됩니다. 하지만 여러 단어를 검색해야 합니다. 여러 단어를 검색하고 하나를 제외하도록 이것을 확장할 수 있습니까?

답변1

foobarand 하지만을 포함하는 파일 이름을 원한다고 가정합니다 .아니요 baz. 이 경우:

find . -type f -exec gawk '
  BEGINFILE{a=b=c=0}
  /foo/{a=1} /bar/{b=1} /baz/{c=1;nextfile}
  ENDFILE{if(a && b && !c)print FILENAME}' {} +

[Linux를 사용하고 있으므로 이미 GNU awk(gawk)에 액세스할 수 있다고 가정합니다. ]

이 접근 방식에서는 가능한 한 적은 수의 awk 호출이 시작되고 각 파일은 한 번만 읽혀집니다. 중간 파일은 필요하지 않습니다. 이것은 작동합니다.

두 개의 파일이 포함된 디렉터리를 고려해 보겠습니다.

$ cat file1.eml 
foo and
bar only
$ cat file2.eml 
foo
and
bar
and
baz

명령을 실행하면 ./file1.eml요구 사항을 충족하는 유일한 파일이 생성됩니다.

$ find . -type f -exec gawk '
    BEGINFILE{a=b=c=0}
    /foo/{a=1} /bar/{b=1} /baz/{c=1;nextfile}
    ENDFILE{if(a && b && !c)print FILENAME}' {} +
./file1.eml

어떻게 작동하나요?

  • find일반 파일 목록을 재귀적으로 수집하여 전달합니다 gawk.

  • BEGINFILE{a=b=c=0}

    그러면 각 새 파일의 시작 부분에 변수 abc0(false)으로 설정됩니다.

  • /foo/{a=1}

    행에 가 포함되어 있으면 foo변수는 a1로 설정됩니다. (진짜).

  • /bar/{b=1}

    행에 가 포함되어 있으면 bar변수는 b1로 설정됩니다. (진짜).

  • /baz/{c=1;nextfile}

    행에 가 포함되어 있으면 baz변수는 c1로 설정됩니다. (진짜).

    제외하려는 단어를 찾은 후에는( baz예제에서처럼) 파일을 더 이상 읽을 필요가 없습니다. 그래서 우리는 nextfile나머지 줄을 건너 뛰고 즉시 ENDFILE로 이동합니다.

  • ENDFILE{if(a && b && !c)print FILENAME}

    각 파일의 끝에서 if ab아니요 c(awk의 논리 !- 아님) 둘 다 true이면 파일 이름을 인쇄합니다.

GNU가 아닌 awk

예를 들어 awk에 좋은 BEGINFILE기능이 없으면 각 파일에 대해 하나씩 실행 해야 합니다 .ENDFILEmawkawk

find . -type f -exec mawk '
  /foo/{a=1} /bar/{b=1} /baz/{c=1;exit}
  END{if(a && b && !c) print FILENAME}' {} \;

또는 (힌트:에드 모튼):

awk 'FNR==1 { if (a && b && !c) print fname; fname=FILENAME; a=b=c=0 } /foo/{a=1} /bar/{b=1} /baz/{c=1}   END{if(a && b && !c) print FILENAME}' *.eml

또는 재귀 검색을 사용하세요.

find . -type f -exec awk 'FNR==1 { if (a && b && !c) print fname; fname=FILENAME; a=b=c=0 } /foo/{a=1} /bar/{b=1} /baz/{c=1}   END{if(a && b && !c) print FILENAME}' {} +

답변2

find -exec다음 을 사용해 보세요 grep -q:

find /my/path -name "*.eml" \
  -exec grep -F -q "word1" {} \; \
  -exec grep -F -q "word2" {} \; \
  -exec grep -F -q "word3" {} \; \
  ! -exec grep -F -q "word4" {} \; \
  -print
  • grep -q상태 코드만 반환
  • 단어가 아닌 패턴을 검색하려면 다음 -F을 생략하세요.grep
  • 전체 단어만 일치하려면 -w추가 : 일치 하지만 일치하지 않음 .grepwordsomeword
  • find명령을 연결 -exec하고 그 중 하나가 실패하면 중지합니다( grep -q오류 코드가 반환되는 경우).

답변3

다음 방법을 사용할 수 있습니다.

grep -rIlZe foo . |
  xargs -r0 grep -lZe bar |
  xargs -r0 grep -LZe baz |
  xargs -r0 cat > MyOutputFile

grep즉, 처음 생성된 파일 목록이 xargs -r0다음 파일 목록에 공급되어 grep목록이 더욱 구체화됩니다.

-L마지막 옵션은 일치 하는 항목이 없는 보고 파일 grep과 유사하므로 다음 을 포함하는 파일 -l로 끝납니다 foo.bar아니요 baz.

-r및 또는 -I첫 번째 항목 만 필수입니다 grep. 후자는 재귀할 디렉터리 가 아닌 일반 파일 목록(바이너리 파일은 -I첫 번째 파일에서 필터링 됨 grep) 을 인수로 사용합니다.r

이는 파일의 내용을 여러 번 읽게 될 수 있음을 의미하며 이는 그다지 효율적이지는 않지만 grep일반적으로 구현이 훨씬 빠르며 awk위의 4개 명령이 모두 병렬로 실행되므로 일부 처리가 여러 번 수행됩니다. 프로세서 동시에 실행되고 데이터가 이미 메모리에 캐시되어 있으므로 awk다음을 기반으로 하는 프로세서보다 빠를 수 있습니다.

답변4

이 코드를 복사하여 새 bash 스크립트 파일에 붙여넣고 chmod +x <file>터미널에서 저장한 후 실행하면 다음이 포함된 모든 파일이 나열됩니다."부자"그리고"술집"그리고 포함하지 않습니다"해적"끈:

#!/bin/bash
function notcontain {
        for FILE in $(find . 2> /dev/null); do
                if ! grep "rab" $FILE > /dev/null 2>&1; then
                        echo $FILE
                fi
        done
}
    
for FILE in `notcontain`; do
        if grep "foo" $FILE > /dev/null 2>&1 | grep "bar" $FILE > /dev/null 2>&1; then
                echo $FILE
        fi
done

도움이 되었기를 바랍니다 :)

관련 정보