grep : 메모리 소진


42

나는 매우 간단한 검색을하고 있었다 :

grep -R Milledgeville ~/Documents

그리고 얼마 후이 오류가 나타났습니다.

grep: memory exhausted

어떻게 피할 수 있습니까?

시스템에 10GB의 RAM이 있고 실행중인 응용 프로그램이 거의 없으므로 간단한 grep에 메모리가 부족하다는 사실에 정말 놀랐습니다. ~/Documents약 100GB이며 모든 종류의 파일이 포함되어 있습니다.

grep -RI 이 문제가 없을 수도 있지만 이진 파일에서도 검색하고 싶습니다.

답변:


46

두 가지 잠재적 인 문제 :

  • grep -R( grepOS / X 10.8 이상 에서 수정 된 GNU 제외 )는 심볼릭 링크를 따르므로 파일에 100GB의 파일 만 있어도 심볼릭 링크 ~/Documents가 여전히있을 수 있으며 /파일을 포함한 전체 파일 시스템을 스캔하게됩니다. 처럼 /dev/zero. 사용하여 grep -r새로운 GNU와 함께 grep, 또는 표준 구문을 사용합니다 :

    find ~/Documents -type f -exec grep Milledgeville /dev/null {} +
    

    (종료 상태는 패턴이 일치하는지 여부를 반영하지 않습니다).

  • grep패턴과 일치하는 선을 찾습니다. 이를 위해 한 번에 한 줄씩 메모리에로드해야합니다. grep다른 많은 grep구현 과 달리 GNU 는 읽은 행의 크기에 제한이 없으며 이진 파일에서 검색을 지원합니다. 따라서 사용 가능한 메모리보다 큰 파일 인 매우 큰 행 (즉, 두 줄 바꿈 문자가 매우 큼)이있는 파일이 있으면 실패합니다.

    일반적으로 스파 스 파일에서 발생합니다. 다음과 같이 재현 할 수 있습니다.

    truncate -s200G some-file
    grep foo some-file
    

    그 중 하나는 해결하기가 어렵습니다. 다음과 같이 할 수 있습니다 (여전히 GNU 사용 grep).

    find ~/Documents -type f -exec sh -c 'for i do
      tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0"
      done' Milledgeville {} +
    

    입력을에 공급하기 전에 NUL 문자 시퀀스를 하나의 개행 문자로 변환합니다 grep. 문제는 스파 스 파일로 인한 문제에 적용됩니다.

    큰 파일에 대해서만 수행하여 최적화 할 수 있습니다.

    find ~/Documents -type f \( -size -100M -exec \
      grep -He Milledgeville {} + -o -exec sh -c 'for i do
      tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0"
      done' Milledgeville {} + \)
    

    파일이 희소 하지 않고grep 이전 버전의 GNU가있는 경우이 옵션을 2.6사용할 수 있습니다 --mmap. 행이 복사되는 대신 행이 메모리에 줄어 듭니다. 즉, 페이지를 파일로 페이징하여 시스템이 항상 메모리를 회수 할 수 있습니다. 이 옵션은 GNU grep2.6 에서 제거되었습니다


실제로 GNU grep은 한 줄로 읽는 것을 신경 쓰지 않고 파일의 많은 부분을 단일 버퍼로 읽습니다. "GNU grep은 입력을 줄 바꿈하는 것을 피합니다." 출처 : lists.freebsd.org/pipermail/freebsd-current/2010-Auggust/…
Godric Seer

4
@ GodricSeer, 여전히 파일의 많은 부분을 단일 버퍼로 읽을 수는 있지만 문자열을 찾지 못하고 줄 바꿈 문자를 찾지 못하면 내 단일 버퍼를 메모리에 유지하는 것이 좋습니다 일치하는 것이 발견되면 표시해야하므로 다음 버퍼를 읽습니다. 따라서 문제는 여전히 동일합니다. 실제로 200GB 스파 스 파일의 grep은 OOM에서 실패합니다.
Stéphane Chazelas

1
@GodricSeer, 안돼. 행이 모두 작 으면 grep지금까지 처리 한 버퍼를 버릴 수 있습니다. 몇 킬로바이트 이상의 메모리를 사용하지 않고 무기한으로 grep출력 할 수 있습니다 yes. 문제 선의 크기입니다.
Stéphane Chazelas

3
GNU grep --null-data옵션도 여기서 유용 할 수 있습니다. 입력 줄 종결 자로 줄 바꿈 대신 NUL을 사용합니다.
iruvar

1
@ 1_CR, 좋은 점이지만 출력 라인 터미네이터도 NUL로 설정합니다.
Stéphane Chazelas

5

나는 보통

find ~/Documents | xargs grep -ne 'expression'

나는 많은 방법을 시도했지만 이것이 가장 빠르다는 것을 알았습니다. 공백이있는 파일은 파일 이름을 잘 처리하지 못합니다. 이 경우에 해당하고 GNU 버전의 grep이있는 경우 다음을 사용할 수 있습니다.

find ~/Documents -print0 | xargs -0 grep -ne 'expression'

그렇지 않은 경우 다음을 사용할 수 있습니다.

 find ~/Documents -exec grep -ne 'expression' "{}" \;

exec모든 파일에 대해 grep 할 것 입니다.


공백이있는 파일에서 중단됩니다.
Chris Down

흠, 그건 사실이야
Kotte September

당신은 그와 함께 주위를 얻을 수 있습니다find -print0 | xargs -0 grep -ne 'expression'
Drav 슬로안

@ChrisDown은 깨진 이식 가능한 솔루션보다는 문제가 아닌 솔루션입니다.
reto

@ChrisDown 대부분의 주요 유니스들이 채택 find -print0하고 xargs -0있으며 지금까지 세 가지 BSD, MINIX 3, Solaris 11,…
Gilles 'SO-stop

4

이 문제를 해결할 수있는 몇 가지 방법을 생각할 수 있습니다.

  • 한 번에 한 파일 씩 모두 파기하지 말고 한 파일 씩 수행하십시오. 예:

    find /Documents -type f -exec grep -H Milledgeville "{}" \;
    
  • 단어가 들어있는 파일 만 알아야하는 경우 grep -l대신 수행하십시오. grep은 첫 번째 히트 후 검색을 중단하므로 큰 파일을 계속 읽을 필요가 없습니다.

  • 실제 텍스트를 원한다면 두 개의 별도 greps을 묶을 수 있습니다.

    for file in $( grep -Rl Milledgeville /Documents ); do grep -H Milledgeville "$file"; done
    

마지막 예제는 유효한 구문이 아닙니다. 명령 대체를 수행해야합니다 ( grep파일 이름에 유효한 구분 기호를 사용하여 출력 하므로이를 수행하지 않아야 함 ). 당신은 또한 인용해야합니다 $file.
Chris Down

후자의 예는 파일 이름에 개행 문자 나 공백이있는 문제로 어려움을 겪습니다 ( for파일을 두 개의 인수로 처리하게됩니다)
Drav Sloan

@DravSloan 편집하는 동안 개선하면서도 여전히 유효한 파일 이름이 손상됩니다.
Chris Down

1
예, 나는 그녀의 대답의 일부이기 때문에 그대로 두었습니다. 파일에 공백 / 줄 바꿈이없는 경우를 위해 그것을 향상시키기 위해 방금 개선하려고했습니다.
Drav Sloan

자신의 수정 -> 그녀, 내 사과 제니 : /
Drav 슬론

1

6TB 디스크를 잃어버린 데이터를 검색하고 메모리가 소진되었습니다. 오류가 발생했습니다. 다른 파일에서도 작동합니다.

우리가 생각해 낸 해결책은 dd를 사용하고 청크를 그렙하여 디스크를 청크로 읽은 것입니다. 이것은 코드 (big-grep.sh)입니다.

#problem: grep gives "memory exhausted" error on 6TB disks
#solution: read it on parts
if [ -z $2 ] || ! [ -e $1 ]; then echo "$0 file string|less -S # greps in chunks"; exit; fi

FILE="$1"
MATCH="$2"

SIZE=`ls -l $1|cut -d\  -f5`
CHUNKSIZE=$(( 1024 * 1024 * 1 )) 
CHUNKS=100 # greps in (100 + 1) x 1MB = 101MB chunks
COUNT=$(( $SIZE / $CHUNKSIZE * CHUNKS ))

for I in `seq 0 $COUNT`; do
  dd bs=$CHUNKSIZE skip=$(($I*$CHUNKS)) count=$(( $CHUNKS+1)) if=$FILE status=none|grep -UF -a --context 6 "$MATCH"
done

1
겹치는 청크 를 읽지 않으면 청크 경계에서 일치하는 항목이 누락 될 수 있습니다. 오버랩은 최소한 일치시킬 문자열보다 커야합니다.
Kusalananda

100MB 청크마다 1MB를 추가로 검색하도록 업데이트되었습니다 ... 저렴한 핵
Dagelf
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.