grep: память исчерпывается

Кластерный SSH.

yum install cssh после активации rpmforge.

42
10.09.2013, 16:28
4 ответа

Две потенциальных проблемы:

  • grep -R (за исключением измененного GNU grep найденный на OS/X 10.8 и выше), следует за символьными ссылками, поэтому даже если существует только 100 ГБ файлов в ~/Documents, могла бы все еще быть символьная ссылка на / например, и Вы закончите тем, что сканировали целую файловую систему включая файлы как /dev/zero. Использовать grep -r с более новым GNU grep, или используйте стандартный синтаксис:

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

    (однако обратите внимание, что статус выхода не отразит то, что шаблон подобран или не).

  • grep находит строки, которые соответствуют шаблону. Для этого это должно загрузить одну строку за один раз в памяти. GNU grep в противоположность многим другой grep реализации не имеют предела на размер строк, он читает и поддерживает поиск в двоичных файлах. Так, если у Вас будет файл с очень большой строкой (то есть, с двумя символами новой строки очень далеко независимо), больше, чем доступная память, то это перестанет работать.

    Это обычно происходило бы с редким файлом. Можно воспроизвести его с:

    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 {} + \)
    

    Если файлы не редки, и у Вас есть версия GNU grep до 2.6, можно использовать --mmap опция. Строки будут отображены в памяти в противоположность скопированному там, что означает, что система может всегда исправлять память подкачкой страниц страницы в файл. Та опция была удалена в GNU grep 2.6

46
27.01.2020, 19:35
  • 1
    На самом деле GNU grep не заботится о чтении в 1 строке, это читает значительную часть файла в единственный буфер. "Кроме того, GNU grep СТАРАЕТСЯ НЕ ПОВРЕЖДАТЬ ВХОД В СТРОКИ". источник: lists.freebsd.org/pipermail/freebsd-current/2010-August / … –  Godric Seer 10.09.2013, 15:32
  • 2
    @GodricSeer, это может все еще считать значительную часть файла в единственный буфер, но если это не имеет, находят строку там, и не нашел символ новой строки также, моя ставка - то, что это сохраняет тот единственный буфер в памяти и читает следующий буфер в, поскольку это должно будет отобразить его, если соответствие найдено. Так, проблемой является все еще то же. На практике grep на редком файле на 200 ГБ действительно перестал работать с OOM. –  Stéphane Chazelas 10.09.2013, 15:44
  • 3
    @GodricSeer, хорошо нет. Если строки являются все маленькими, grep может отбросить буферы, которые это обработало до сих пор. Вы можете grep вывод yes неограниченно долго не используя больше, чем несколько килобайтов памяти. Проблемой является размер строк. –  Stéphane Chazelas 10.09.2013, 15:51
  • 4
    GNU grep --null-data опция может также быть полезной здесь. Это вызывает использование NUL вместо новой строки как входной разделитель строки. –  iruvar 16.09.2013, 18:27
  • 5
    @1_CR, положительная сторона, хотя это также устанавливает выходной разделитель строки на NUL. –  Stéphane Chazelas 16.09.2013, 18:35

Я обычно делаю

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

Я попробовал набор методов и нашел, что это было самым быстрым. Обратите внимание, что это не обрабатывает файлы с пробелами имя файла очень хорошо. Если Вы знаете дело обстоит так и имеете версию GNU grep, можно использовать:

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

Если не можно использовать:

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

Который будет exec grep для каждого файла.

5
27.01.2020, 19:35
  • 1
    Это повредится на файлах с пробелами. –  Chris Down 10.09.2013, 14:04
  • 2
    Хм, это верно. –  Kotte 10.09.2013, 14:08
  • 3
    можно обойти это с find -print0 | xargs -0 grep -ne 'expression' –  Drav Sloan 10.09.2013, 14:09
  • 4
    @ChrisDown скорее решение непротаблицы, чем поврежденный - портативное решение. –  reto 10.09.2013, 19:41
  • 5
    @ChrisDown канала, Большинство главных нельдов приняло find -print0 и xargs -0 к настоящему времени: все три BSD, MINIX 3, Солярис 11, … –  Gilles 'SO- stop being evil' 11.09.2013, 00:21

Я могу думать о нескольких способах обойти это:

  • Вместо того, чтобы захватить все файлы сразу, сделайте один файл за один раз. Пример:

    find /Documents -type f -exec grep -H Milledgeville "{}" \;
    
  • Если только необходимо знать, какие файлы содержат слова, сделать grep -l вместо этого. Так как grep там прекратит искать после первого хита это не должно будет продолжать читать любые огромные файлы

  • Если Вы действительно хотите фактический текст также, Вы могли бы обмануть двум отдельным властям:

    for file in $( grep -Rl Milledgeville /Documents ); do grep -H Milledgeville "$file"; done
    
4
27.01.2020, 19:35
  • 1
    Последним примером не является допустимый синтаксис - необходимо было бы выполнить замену команды (и Вы не должны делать этого с тех пор grep выводы с помощью разделителя, который законен в именах файлов). Также необходимо заключить в кавычки $file. –  Chris Down 10.09.2013, 14:05
  • 2
    Последний пример страдает с проблемой имен файлов, имеющих новую строку или пробел в них, (это вызовет for обработать файл как два аргумента) –  Drav Sloan 10.09.2013, 14:12
  • 3
    @DravSloan Ваше редактирование, в то время как улучшение, все еще повреждения на легальных именах файлов. –  Chris Down 10.09.2013, 14:19
  • 4
    Да я оставил его внутри, потому что это была часть ее ответа, я просто попытался улучшить его так, это будет работать (для случаев, где нет никаких пробелов/новых строк и т.д. в файлах). –  Drav Sloan 10.09.2013, 14:34
  • 5
    его-> ее, мои извинения Jenny:/ –  Drav Sloan 10.09.2013, 14:38

Я просматриваю диск объемом 6 ТБ для поиска потерянных данных и получаю ошибку «Память исчерпана» -. Это должно работать и для других файлов.

Решение, которое мы придумали, состояло в том, чтобы читать диск порциями с помощью 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
3
20.08.2021, 13:06

Теги

Похожие вопросы