두 개의 서로 다른 단어(순서, 행)가 있는지 텍스트 파일을 검색합니다.

두 개의 서로 다른 단어(순서, 행)가 있는지 텍스트 파일을 검색합니다.

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

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

내가 겪고 있는 문제는 "FIND"와 "ME" 사이에 공백이 없으면 검색 결과에서 파일이 생성되지 않는다는 것입니다. "FIND ME" 대신 "FIND"와 "ME"가 모두 존재하는 파일에서 사전 검색 문자열을 어떻게 조정할 수 있나요?

저는 AIX를 사용하고 있습니다.

답변1

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그러나 이로 인해 파일당 최대 2개의 가 실행 됩니다 . 너무 많은 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 }'
}

export LC_ALL=C
find .//. -type f |
  convert_to_xargs |
  xargs grep -l FIND |
  convert_to_xargs |
  xargs grep -l ME

아이디어는 xargs의 출력을 findxargs에 적합한 형식으로 변환하는 것입니다(여기에는 공백(로케일의 경우 SPC/TAB/NL C, 다른 로케일의 YMMV)으로 구분된 단어 목록이 필요합니다. 여기서 작은따옴표, 큰따옴표 및 백슬래시는 공백을 이스케이프할 수 있습니다. 및 기타).

일반적으로 의 출력은 후처리할 수 없습니다 find -print. 왜냐하면 파일 이름을 줄 바꿈으로 구분하고 파일 이름에 있는 줄 바꿈을 이스케이프하지 않기 때문입니다. 예를 들어 다음과 같은 경우가 있습니다.

./a
./b

b호출된 디렉터리에서 하나의 파일을 호출하는지, 아니면 a<NL>.현재 디렉터리에서 두 개의 파일을 호출 하는지 알 수 없습니다 .ab

를 사용하면 파일 경로에 출력으로 표시될 수 없기 .//.때문에 (빈 이름을 가진 디렉터리는 존재하지 않고 파일 이름에 허용되지 않기 때문에) 가 포함된 줄이 보이면 그것이 다음임을 알 수 있습니다. new 파일 이름의 첫 번째 줄입니다. 그러므로 우리는 이 명령을 사용하여 이 줄 앞의 줄을 제외한 모든 줄 바꿈을 이스케이프 처리할 수 있습니다.//find///awk

위의 예를 취하면 find출력은 첫 번째 경우(파일 1개)가 됩니다.

.//a
./b

awk는 다음으로 이스케이프됩니다.

.//a\
./b

따라서 이것은 xargs인수로 간주됩니다. 두 번째 경우(두 개의 파일):

.//a
.//b

이는 awk그대로 유지되므로 xargs두 매개변수를 모두 참조하세요.

임의의 바이트 시퀀스로 작업하려면(사용자 로케일에서 유효한 문자를 형성하지 않더라도) 단순화하기 위해 LC_ALL=C그렇게 sed( awk및 일부 구현)가 필요합니다.xargs공백SPC와 TAB만 정의하고 백슬래시가 포함된 인코딩된 문자를 서로 다르게 해석하는 여러 유틸리티의 문제를 방지하세요.

답변2

파일이 단일 디렉토리에 있고 이름에 공백, 탭, 줄 바꿈 또는 문자가 포함되지 않고 *또는 로 시작 하지 않는 경우 ?ME 를 포함하는 파일 목록을 가져온 다음 FIND도 포함하도록 범위를 좁힙니다.[-.

grep -l FIND `grep -l ME *`

답변3

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}' {} +

답변4

허용되는 답변을 보면 필요한 것보다 더 복잡해 보입니다. GNU 버전은 NULL로 끝나는 문자열을 지원 find합니다 . 매우 간단합니다:grepxargs

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 버전이 없으면 허용되는 답변을 사용하십시오.

관련 정보