![두 개의 서로 다른 단어(순서, 행)가 있는지 텍스트 파일을 검색합니다.](https://linux55.com/image/29817/%EB%91%90%20%EA%B0%9C%EC%9D%98%20%EC%84%9C%EB%A1%9C%20%EB%8B%A4%EB%A5%B8%20%EB%8B%A8%EC%96%B4(%EC%88%9C%EC%84%9C%2C%20%ED%96%89)%EA%B0%80%20%EC%9E%88%EB%8A%94%EC%A7%80%20%ED%85%8D%EC%8A%A4%ED%8A%B8%20%ED%8C%8C%EC%9D%BC%EC%9D%84%20%EA%B2%80%EC%83%89%ED%95%A9%EB%8B%88%EB%8B%A4..png)
동일한 파일에 두 개의 단어 인스턴스가 있는지 파일을 검색하는 방법을 찾고 있습니다. 지금까지 검색을 수행하기 위해 다음을 사용했습니다.
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의 출력을 find
xargs에 적합한 형식으로 변환하는 것입니다(여기에는 공백(로케일의 경우 SPC/TAB/NL C
, 다른 로케일의 YMMV)으로 구분된 단어 목록이 필요합니다. 여기서 작은따옴표, 큰따옴표 및 백슬래시는 공백을 이스케이프할 수 있습니다. 및 기타).
일반적으로 의 출력은 후처리할 수 없습니다 find -print
. 왜냐하면 파일 이름을 줄 바꿈으로 구분하고 파일 이름에 있는 줄 바꿈을 이스케이프하지 않기 때문입니다. 예를 들어 다음과 같은 경우가 있습니다.
./a
./b
b
호출된 디렉터리에서 하나의 파일을 호출하는지, 아니면 a<NL>.
현재 디렉터리에서 두 개의 파일을 호출 하는지 알 수 없습니다 .a
b
를 사용하면 파일 경로에 출력으로 표시될 수 없기 .//.
때문에 (빈 이름을 가진 디렉터리는 존재하지 않고 파일 이름에 허용되지 않기 때문에) 가 포함된 줄이 보이면 그것이 다음임을 알 수 있습니다. 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보다 크면 인쇄가 더 빠르고 효율적입니다.FIND
ME
END
FILENAME
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
합니다 . 매우 간단합니다:grep
xargs
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 버전이 없으면 허용되는 답변을 사용하십시오.