내 이메일 백업에서 중요한 이메일을 검색하려고 합니다. 수천 개의 파일이 포함된 하위 디렉터리가 있는 디렉터리입니다 .eml
(Linux 파일 시스템의 경우). .eml
세 단어가 포함된 텍스트 파일을 검색하고 한 단어를 제외하고 싶습니다 .
먼저 한 단어를 검색한 다음 파이프를 통해 다른 단어를 검색해 봅니다.
grep -R 'foo' ~/Directory/path | grep 'bar'
이는 같은 줄에 두 단어가 포함된 파일만 반환하기 때문에 작동하지 않습니다. 전체 파일에 두 단어가 포함된 파일이 필요합니다.
나는 단어가 포함된 파일을 찾고 파일 내용을 출력 파일로 연결하려고 합니다.
grep -rIlZ '.' -e 'foo' | xargs -0 cat > MyOutputFile
맥락을 볼 수 있기 때문에 도움이 됩니다. 하지만 여러 단어를 검색해야 합니다. 여러 단어를 검색하고 하나를 제외하도록 이것을 확장할 수 있습니까?
답변1
foo
bar
and 하지만을 포함하는 파일 이름을 원한다고 가정합니다 .아니요 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}
그러면 각 새 파일의 시작 부분에 변수
a
및b
가c
0(false)으로 설정됩니다./foo/{a=1}
행에 가 포함되어 있으면
foo
변수는a
1로 설정됩니다. (진짜)./bar/{b=1}
행에 가 포함되어 있으면
bar
변수는b
1로 설정됩니다. (진짜)./baz/{c=1;nextfile}
행에 가 포함되어 있으면
baz
변수는c
1로 설정됩니다. (진짜).제외하려는 단어를 찾은 후에는(
baz
예제에서처럼) 파일을 더 이상 읽을 필요가 없습니다. 그래서 우리는nextfile
나머지 줄을 건너 뛰고 즉시 ENDFILE로 이동합니다.ENDFILE{if(a && b && !c)print FILENAME}
각 파일의 끝에서 if
a
및b
아니요c
(awk의 논리!
- 아님) 둘 다 true이면 파일 이름을 인쇄합니다.
GNU가 아닌 awk
예를 들어 awk에 좋은 BEGINFILE
기능이 없으면 각 파일에 대해 하나씩 실행 해야 합니다 .ENDFILE
mawk
awk
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
추가 : 일치 하지만 일치하지 않음 .grep
word
someword
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
도움이 되었기를 바랍니다 :)