Grep은 패턴 파일의 모든 항목과 일치합니다.

Grep은 패턴 파일의 모든 항목과 일치합니다.

패턴 파일이 있고 이를 파일 디렉터리와 비교하고 싶습니다.

패턴 파일 내용은 다음과 같습니다(정규 표현식일 수도 있음).

pattern-that-occurs-in-file
pattern-that-also-occurs-in-file

콘텐츠가 패턴과 일치하는 경우 표시되어야 하는 검색 파일의 예:

unrelated content
pattern-that-occurs-in-file
more unrelated content
pattern-that-also-occurs-in-file
further unrelated content

또는:

unrelated content
pattern-that-also-occurs-in-file
more unrelated content
pattern-that-occurs-in-file
further unrelated content

샘플 검색 파일은 다음과 같습니다.아니요이리와:

unrelated content
more unrelated content
pattern-that-occurs-in-file
further unrelated content

또는:

unrelated content
pattern-that-also-occurs-in-file
more unrelated content
further unrelated content

또는:

unrelated content
more unrelated content
further unrelated content

두 가지 패턴이 나타나는 파일 목록을 출력하려면 grep이 필요합니다. 일치하는 선이 보이더라도 상관 없습니다.

단일 명령으로 이 작업을 수행할 수 있습니까? 그렇다면 어떻게 해야 할까요?

답변1

정확한 명령은 아니지만 다음과 같습니다.

num_patterns=$( wc -l < patterns_file )
for file in dir/*; do
    num_occurrances=$( grep -F -o -f patterns_file "$file" | sort -u | wc -l )
    if (( num_patterns == num_occurrances )); then
        echo "all patterns in $file"
    fi
done

패턴이 정규식인 경우에는 일치 텍스트가 모든 일치 항목에 대해 고유하지 않을 수 있으므로 이 방법은 작동하지 않습니다.

답변2

./*.txt관심 있는 모든 파일이 일치하고 다음을 포함하는 파일을 찾고 싶다고 가정해 보겠습니다.모두~의파일에서 ./patterns(세 줄 이상 포함될 수 있음):

#!/bin/bash

pathnames=( ./*.txt )

while IFS= read -r pattern; do
    for pathname in "${pathnames[@]}"; do
        pathnames=( ${pathnames[@]:1} )

        if grep -qF -e "$pattern" "$pathname"; then
            pathnames+=( "$pathname" )
        fi
    done
done < ./patterns

printf 'Matched: %s\n' "${pathnames[@]}"

그러면 패턴이 순환됩니다. 각 패턴에 대해 배열의 모든 파일을 테스트합니다 pathnames. 패턴이 일치하면 현재 경로 이름을 배열에 유지하고, 그렇지 않으면 폐기합니다. 마지막으로 pathnames모든 패턴을 포함하는 경로 이름만 포함됩니다.

pathnames어레이가 관리되는 방식 으로 인해 grep더 많은 파일이 삭제됨에 따라 각 패턴에 대한 호출 수가 감소합니다.

이 명령은 pathnames=( ${pathnames[@]:1} )배열에서 첫 번째(현재) 경로 이름을 제거하고 pathnames+=( "$pathname" )끝에 다시 배치합니다.

이 명령 grep -qF -e "$pattern" "$pathname"진짜파일 $pathname$pattern. -qmake Quiet을 사용 grep하고 파일의 패턴과 일치하면 즉시 종료되도록 합니다. -F정규식 일치 대신 문자열 비교에 사용합니다 .


sh저는 명명된 배열보다 간결한 구문을 선호하기 때문에 bash위의 변형이 있습니다 /bin/sh(위치 매개변수가 pathnames배열을 대체함).

#!/bin/sh

set -- ./*.txt

while IFS= read -r pattern; do
    for pathname do
        shift

        if grep -qF -e "$pattern" "$pathname"; then
            set -- "$@" "$pathname"
        fi
    done
done < ./patterns

printf 'Matched: %s\n' "$@"

답변3

내가 올바르게 이해했다면 이것이 옵션이 될 수 있습니다(내 논리가 타당하다면). 여기서는 패턴이 각 파일에서 고유하다고 가정합니다.

grep -R < file_with_patterns . | cut -d':' -f1 | uniq -d

grep두 패턴이 일치하면 두 행을 반환하거나 한 행만 반환하거나 아무것도 반환하지 않습니다. 이러한 상황을 활용하여 uniq -d파일 이름에 대해 중복된 결과만 표시합니다.

답변4

@glenn-jackman 및 @schrodigerscatcuriosity의 답변은 정규식을 통과하지 못했습니다(OP는 정규식도 포함하도록 질문을 수정했습니다). 예를 들어 패턴은 1.파일의 "1a" 및 "1b"와 일치하지만 패턴은 2.아무것도 일치하지 않지만 두 알고리즘 모두 파일이 두 패턴과 일치한다고 결론을 내립니다. 둘째, 패턴은 123"1234"와 일치하지만 12일치하는 패턴으로 인해 grep이 추가 출력을 생성하지 않습니다. 두 알고리즘 모두 파일이 두 패턴 중 하나만 일치한다고 결론을 내립니다.

@kusalananda는 잘 작동하지만 더 효율적인 솔루션이 있을 수 있습니다.

files=`find ./*.txt`
while read pattern; do
    files=`echo "$files" | xargs grep -l "$pattern"` || break
done < ./patterns
echo Matched: $files

이 솔루션은 @kusalananda의 솔루션과 유사합니다. 즉, 패턴을 반복하면서 일치하지 않는 파일을 모두 제거합니다. 그러나 이 솔루션은 xargs grep -l중첩 루프 대신 파일을 사용합니다. 따라서 대략적으로 파일당 패턴당 하나의 grep 프로세스를 실행하는 대신 패턴당 하나의 grep 프로세스를 실행하므로 훨씬 더 빨라야 합니다.

추신: 이 솔루션은 파일 이름의 공백을 처리하지 않지만 @kusalananda는 처리합니다. 그러나 이 솔루션은 파일 이름의 공백을 처리하도록 쉽게 수정할 수 있습니다. 파일 이름에 공백이나 기타 잘못된 문자가 있으면 먼저 부끄러워서 머리를 숙이고 두 번째로 변경하십시오.

xargs

도착하다

tr \\n \\0 | xargs -0

이것이 혼란스럽고 주요 문제와 관련이 없어 보이기 때문에 이것을 주요 해결책으로 포함시키지 않았습니다.

PPS: 최대 속도를 위해 가장 희귀한 패턴을 패턴 파일에 먼저 배치하고 가장 일반적인 패턴을 마지막에 배치하여 가능한 한 많은 파일을 초기에 제거합니다.

관련 정보