[unix] 서로 다른 두 단어가있는 파일을 검색하는 방법은 무엇입니까?

두 단어 인스턴스가 동일한 파일에 존재하는 파일을 검색하는 방법을 찾고 있습니다. 지금까지 다음을 사용하여 검색을 수행했습니다.

find . -exec grep -l "FIND ME" {} \;

내가 겪고있는 문제는 “FIND”와 “ME”사이에 정확히 하나의 공간이 없으면 검색 결과가 파일을 생성하지 않는다는 것입니다. “FIND ME”와 반대로 “FIND”와 “ME라는 단어가 모두 파일에있는 이전 검색 문자열을 어떻게 적용합니까?

AIX를 사용하고 있습니다.



답변

GNU 도구로 :

find . -type f  -exec grep -lZ FIND {} + | xargs -r0 grep -l ME

당신은 표준 적으로 할 수 있습니다 :

find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;

그러나 그것은 파일 당 두 개의 grep을 실행합니다. grep파일 이름에 문자를 허용하면서 많은 수 의 실행을 피하고 이식성을 유지하려면 다음을 수행하십시오.

convert_to_xargs() {
  sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
    {
      if (NR > 1) {
        printf "%s", line
        if (!index($0, "//")) printf "\\"
        print ""
      }
      line = $0
    }'
    END { print line }'
}

find .//. -type f |
  convert_to_xargs |
  xargs grep -l FIND |
  convert_to_xargs |
  xargs grep -l ME

작은 따옴표, 큰 따옴표 및 백 슬래시로 구분할 수있는 단어 목록 find을 xargs (공백 (SPC / TAB / NL 및 일부 구현이있는 로케일의 다른 공란이 예상 됨)가 예상 됨)에 적합한 형식으로 출력을 변환하는 아이디어 xargs공백과 서로 탈출).

일반적으로 find -print파일 이름을 줄 바꿈 문자로 분리하고 파일 이름에서 찾은 줄 바꿈 문자를 이스케이프하지 않기 때문에 의 출력을 후 처리 할 수 ​​없습니다 . 예를 들면 다음과 같습니다.

./a
./b

우리는 그것이라는 하나 개의 파일인지 알 수있는 방법이 없어 한 b라는 디렉토리를 a<NL>.하거나이 두 파일을인지 a하고 b.

사용하여 .//.있기 때문에, //캔에 의해 출력으로 파일 경로 그렇지 않으면 나타나지 않습니다 find(이 빈 이름을 가진 디렉토리 같은 것은 없습니다하기 때문에 /파일 이름에 사용할 수 없습니다), 우리가 알고있는 우리는이 포함 된 라인을 보면 //다음의 것을, 새 파일 이름의 첫 번째 줄 따라서이 awk명령을 사용 하여 줄 바꿈 문자를 제외한 모든 줄 바꿈 문자를 이스케이프 할 수 있습니다 .

위의 예 find를 보면 첫 번째 경우 (한 파일)로 출력됩니다.

.//a
./b

어느 awk가 탈출합니까?

.//a\
./b

그래서 xargs그것은 그것을 하나의 논쟁으로 간주합니다. 두 번째 경우 (파일 두 개) :

.//a
.//b

어느 awk것이 그대로 xargs남았는지 두 가지 주장이 있습니다.


답변

파일이 하나의 디렉토리에 자신의 이름을 공백, 탭, 개행 문자가 포함되지 않은 경우 *, ?[문자와 함께 시작하지 -않으며 .,이 ME가 포함 된 파일의 목록을 얻을 것이다, 다음 사람에게 그 아래로 범위를 좁힐 그 FIND도 포함합니다.

grep -l FIND `grep -l ME *`


답변

로에게 awk당신은 또한 실행할 수 있습니다 :

find . -type f  -exec awk 'BEGIN{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; END{if (cx > 0 && cy > 0) print FILENAME}' {} \;

및를 사용 cx하여 각각 cy일치하는 행을 계산합니다 . 에서는 블록 모두 카운터> 0 인 경우, 인쇄 .
이것은 다음과 같이 더 빠르고 효율적입니다 .FINDMEENDFILENAME
gnu awk

find . -type f  -exec gawk 'BEGINFILE{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; ENDFILE{if (cx > 0 && cy > 0) print FILENAME}' {} +


답변

또는 사용 egrep -e또는grep -E 이것을 좋아하십시오 :

find . -type f -exec egrep -le '(ME.*FIND|FIND.*ME)' {} \;

또는

find . -type f -exec grep -lE '(ME.*FIND|FIND.*ME)' {} +

+차종은 (지원되는 경우) 찾을 명령 존재에 인수로 이름을 여러 개의 파일 (경로)를 추가-exec 에디션. 이렇게하면 프로세스가 절약되고 \;발견 된 각 파일에 대해 명령을 한 번 호출하는 것보다 훨씬 빠릅니다 .

-type f 디렉토리와의 충돌을 피하기 위해 파일과 만 일치합니다.

'(ME.*FIND|FIND.*ME)'“ME”와 “FIND”또는 “FIND”와 “ME”를 포함하는 모든 행과 일치하는 정규식입니다. 쉘이 특수 문자를 해석하지 못하도록 작은 따옴표.

a를 -i받는grep대소 문자를 구분하지 않으려면 명령에 .

“FIND”가 “ME”앞에 오는 행만 일치 시키려면을 사용하십시오 'FIND.*ME'.

단어 사이에 공백 (1 개 이상 필요)을 요구하려면 : 'FIND +ME'

단어 사이에 공백 (0 이상)을 허용하려면 다음을 수행하십시오. 'FIND *ME'

조합은 정규 표현식으로 끝이 없으며 한 번에 한 행 씩만 일치하는 데 관심이 있다면 egrep은 매우 강력합니다.


답변

허용 된 답변을 보면 필요한 것보다 더 복잡해 보입니다. 의 GNU 버전 findgrepxargs지원 NULL로 끝나는 문자열. 다음과 같이 간단합니다.

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l ME

find원하는 파일로 필터링하도록 명령을 수정할 수 있으며 모든 문자가 포함 된 파일 이름과 함께 작동합니다. 추가 된 sed구문 분석 없이 파일을 추가로 처리 --null하려면 마지막 파일에 다른 파일을 추가하십시오grep

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l --null ME | xargs -0 echo

그리고 함수로서 :

find_strings() {
    find . -type f -print0 | xargs -0 grep -l --null "$1" | xargs -0 grep -l "$2"
}

이러한 도구의 GNU 버전을 실행하지 않는 경우 허용되는 대답을 사용하십시오.


답변