Estaba haciendo una búsqueda muy simple:

grep -R Milledgeville ~/Documents 

Y después de un tiempo apareció este error:

grep: memory exhausted 

¿Cómo puedo evitar esto?

Tengo 10GB de RAM en mi sistema y pocas aplicaciones corriendo, así que estoy realmente sorprendido de que un simple grep se quede sin memoria. ~/Documents tiene aproximadamente 100 GB y contiene todo tipo de archivos.

grep -RI puede que no tenga este problema, pero quiero para buscar en archivos binarios también.

Respuesta

Dos problemas potenciales:

  • grep -R (excepto para el GNU grep modificado que se encuentra en OS / X 10.8 y superior) sigue enlaces simbólicos, por lo que incluso si solo hay 100 GB de archivos en ~/Documents, es posible que todavía haya un enlace simbólico a /, por ejemplo, y terminarás escaneando todo el sistema de archivos, incluido archivos como /dev/zero. Use grep -r con la nueva grep de GNU, o use la sintaxis estándar:

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

    (sin embargo, tenga en cuenta que el estado de salida no reflejará el hecho de que el patrón coincida o no).

  • grep encuentra las líneas que coinciden con el patrón. Para eso, tiene que cargar una línea a la vez en la memoria. GNU grep a diferencia de muchos otros grep Las implementaciones no tienen un límite en el tamaño de las líneas que lee y admiten la búsqueda en archivos binarios. Por lo tanto, si tiene un archivo con una línea muy grande (es decir, con dos caracteres de nueva línea muy alejados), más grande que la memoria disponible, fallará.

    Eso suele suceder con un archivo disperso. Puede reproducirlo con:

    truncate -s200G some-file grep foo some-file 

    Es difícil solucionarlo. Podría hacerlo como (aún con GNU grep):

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

    Eso convierte secuencias de caracteres NUL en un carácter de nueva línea antes de alimentar la entrada a grep. Eso cubriría los casos en los que el problema se debe a archivos dispersos.

    Puede optimizarlo haciéndolo solo para archivos grandes:

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

    Si los archivos no son escasos y tiene una versión de GNU grep anterior a 2.6, puede usar la opción --mmap. Las líneas se mmapearán en la memoria en lugar de copiar allí, lo que significa que el sistema siempre puede recuperar la memoria y paginando las páginas del archivo. Esa opción fue eliminada en GNU grep 2.6

Comentarios

  • @GodricSeer, aún puede leer una gran parte del archivo en un solo búfer, pero si no ‘ no ha encontrado la cadena allí y no ha ‘ t encontré un carácter de nueva línea tampoco, mi apuesta es que mantiene ese único búfer en la memoria y lee el siguiente búfer, ya que tendrá que mostrarlo si se encuentra una coincidencia. Entonces, el problema sigue siendo el mismo. En la práctica, un grep en un archivo disperso de 200GB falla con OOM.
  • @GodricSeer, bueno, no. Si todas las líneas son pequeñas, grep puede descartar los búferes que ha procesado hasta ahora. Puede grep la salida de yes indefinidamente sin utilizar más de unos pocos kilobytes de memoria. El problema es el tamaño de las líneas.
  • La opción GNU grep --null-data también puede ser útil aquí. Obliga al uso de NUL en lugar de newline como terminador de línea de entrada.
  • @ 1_CR, buen punto, aunque eso también establece el terminador de línea de salida en NUL.
  • ¿El fold ayuda con el comando en esas situaciones? Por ejemplo, piense en dd if=/dev/sda | fold -b $((4096*1024*1024)) | grep -a "some string" para limitar la cantidad de memoria requerida a 4GB

Respuesta

Normalmente lo hago

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

Probé varios métodos y descubrí que este era el más rápido. Tenga en cuenta que esto no maneja muy bien archivos con espacios en el nombre del archivo. Si sabe que este es el caso y tiene una versión GNU de grep, puede usar:

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

De lo contrario, puede usar:

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

Lo cual exec un grep para cada archivo.

Comentarios

  • Esto romperá los archivos con espacios.
  • Hmm, eso es cierto.
  • Puede solucionar eso con find -print0 | xargs -0 grep -ne 'expression'
  • @ChrisDown en lugar de una solución no portátil que una solución portátil rota.
  • @ChrisDown Most los principales unices han adoptado find -print0 y xargs -0 ahora: los tres BSD, MINIX 3, Solaris 11,…

Respuesta

Puedo pensar en algunas formas de solucionar esto:

  • En su lugar de grepping todos los archivos a la vez, haz un archivo a la vez.Ejemplo:

    find /Documents -type f -exec grep -H Milledgeville "{}" \; 
  • Si solo necesita saber qué archivos contienen las palabras, haga grep -l en su lugar. Dado que grep dejará de buscar después del primer resultado, no tendrá que seguir leyendo archivos grandes

  • Si también desea el texto real, puede encadenar dos greps separados a lo largo:

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

Comentarios

  • El último ejemplo no es una sintaxis válida; ‘ d necesita realizar una sustitución de comando (y no debe ‘ hacer eso, ya que grep salidas usando un delimitador que es legal en los nombres de archivo). También debe citar $file.
  • El último ejemplo sufre con el problema de los nombres de archivo que tienen una nueva línea o espacios en blanco, (hará que for procese el archivo como dos argumentos)
  • @DravSloan Tu edición una mejora, todavía se rompe en los nombres de archivos legales.
  • Sí, lo dejé porque era parte de su respuesta, solo traté de mejorarlo para que se ejecutara (para los casos en los que s sin espacios / líneas nuevas, etc. en los archivos).
  • Correcciones de su – > ella, mis disculpas Jenny: /

Respuesta

Estoy usando un disco de 6TB para buscar datos perdidos y se agotó la memoria -error. Esto también debería funcionar para otros archivos.

La solución que se nos ocurrió fue leer el disco en fragmentos usando dd y grepping los fragmentos. Este es el código (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 

Comentarios

  • A menos que lea fragmentos superpuestos , es posible que se pierdan coincidencias en los límites del fragmento. La superposición debe ser al menos tan grande como la cadena que espera que coincida.
  • Actualizado para buscar 1 MB extra en cada fragmento de 100 MB … truco barato

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *