Grep en miles de archivos

13

Tengo un directorio con cca 26 000 archivos y necesito grep en todos estos archivos. El problema es que lo necesito lo más rápido posible, por lo que no es ideal hacer un script donde grep tome el nombre de un archivo del comando find y escriba coincidencias en el archivo. Antes del problema "la lista de argumentos es demasiado larga", tomó aproximadamente 2 minutos para grep en todos estos archivos. ¿Alguna idea de como hacerlo? editar: hay un script que está creando nuevos archivos todo el tiempo, por lo que no es posible poner todos los archivos en diferentes directorios.

usuario2778979
fuente
1
usar findcon xargsogrep -R
Eddy_Em
Funciona bien, pero lleva 10 minutos ...
user2778979

Respuestas:

19

Con find:

cd /the/dir
find . -type f -exec grep pattern {} +

( -type fes buscar solo en archivos normales (también excluyendo enlaces simbólicos incluso si apuntan a archivos normales). Si desea buscar en cualquier tipo de archivo excepto directorios (pero tenga cuidado, hay algunos tipos de archivos como fifos o / dev / zero que generalmente no desea leer), reemplace -type fcon el específico de GNU ! -xtype d( -xtype dcoincidencias para archivos de tipo directorio después de la resolución del enlace simbólico)).

Con GNU grep:

grep -r pattern /the/dir

(pero tenga en cuenta que a menos que tenga una versión reciente de GNU grep, seguirá los enlaces simbólicos cuando descienda a los directorios). No se buscarán archivos no regulares a menos que agregue una -D readopción. Sin grepembargo, las versiones recientes de GNU aún no buscarán dentro de enlaces simbólicos.

Las versiones muy antiguas de GNU findno admitían la {} +sintaxis estándar , pero allí podría usar la no estándar:

cd /the/dir &&
  find . -type f -print0 | xargs -r0 grep pattern

Es probable que las actuaciones estén vinculadas a E / S. Ese es el momento de hacer la búsqueda, sería el tiempo necesario para leer todos esos datos del almacenamiento.

Si los datos están en una matriz de discos redundante, leer varios archivos a la vez podría mejorar el rendimiento (y de lo contrario podría degradarlos). Si los rendimientos no están vinculados a E / S (porque, por ejemplo, todos los datos están en caché), y tiene varias CPU, la concurrencia también grepspodría ayudar. Puede hacerlo con xargsla -Popción de GNU .

Por ejemplo, si los datos están en una matriz RAID1 con 3 unidades, o si los datos están en caché y tiene 3 CPU cuyo tiempo de sobra:

cd /the/dir &&
  find . -type f -print0 | xargs -n1000 -r0P3 grep pattern

(aquí se usa -n1000para generar un nuevo grepcada 1000 archivos, hasta 3 que se ejecutan en paralelo a la vez).

Sin embargo, tenga en cuenta que si la salida de grepse redirige, terminará con una salida mal entrelazada de los 3 grepprocesos, en cuyo caso es posible que desee ejecutarlo como:

find . -type f -print0 | stdbuf -oL xargs -n1000 -r0P3 grep pattern

(en un sistema GNU o FreeBSD reciente) o use la --line-bufferedopción de GNU grep.

Si patternes una cadena fija, agregar la -Fopción podría mejorar las cosas.

Si no se trata de datos de caracteres de varios bytes, o si coincide con ese patrón, no importa si los datos son de caracteres de varios bytes o no, entonces:

cd /the/dir &&
  LC_ALL=C grep -r pattern .

podría mejorar el rendimiento significativamente.

Si terminas haciendo tales búsquedas con frecuencia, entonces puedes querer indexar tus datos usando uno de los muchos motores de búsqueda disponibles.

Stéphane Chazelas
fuente
3

26000 archivos en un solo directorio es mucho para la mayoría de los sistemas de archivos. Es probable que se tome una parte importante del tiempo leyendo este gran directorio. Considere dividirlo en directorios más pequeños con solo unos pocos cientos de archivos cada uno.

Llamar findno puede explicar el bajo rendimiento a menos que lo hagas mal. Es una forma rápida de atravesar un directorio y de garantizar que no se arriesgue a intentar ejecutar una línea de comando que sea demasiado larga. Asegúrese de usar -exec grep PATTERN {} +, que empaqueta tantos archivos como sea posible por invocación de comando, y no -exec grep PATTERN {} \;, que se ejecuta grepuna vez por archivo: es probable que la ejecución del comando una vez por archivo sea significativamente más lenta.

Gilles 'SO- deja de ser malvado'
fuente
Gracias, buscaré en Google algo al respecto y probablemente lo dividiré. Hice exactamente lo que está escribiendo y tomó 3 veces más tiempo que solo grep ...
user2778979
Gilles, ¿estás diciendo que el rendimiento diferiría significativamente para 26,000 archivos en un directorio versus 26,000 archivos distribuidos en, digamos, 100 directorios?
user001
1
@ user001 Sí. Cuánto difieren depende del sistema de archivos y posiblemente del almacenamiento subyacente, pero esperaría que cualquier sistema de archivos sea mucho más rápido con 260 archivos en cada uno de los 100 directorios en comparación con 26000 archivos en un solo directorio.
Gilles 'SO- deja de ser malvado'
Gracias por la aclaración. Le pregunté a un seguimiento pregunta en este punto con el fin de comprender el fundamento de la discrepancia.
user001
0

Si necesita agrupar TODOS los archivos varias veces (como dijo, ejecutando un script), le sugiero que busque en los discos RAM, copie todos los archivos allí y luego agrupe los archivos varias veces, esto acelerará su búsqueda por un factor de al menos 100x.

Solo necesitas suficiente carnero. De lo contrario, debe buscar indexar los archivos, por ejemplo. en lucene o en una base de datos nosql y luego ejecuta consultas sobre eso.

Tobias Feldballe
fuente
Como se señaló en otra parte, esto no ayuda al hecho de que hay demasiados archivos contra los que ejecutar grep. También está el punto de que: "hay un script que está haciendo nuevos archivos todo el tiempo, por lo que no es posible poner todos los archivos en directorios diferentes".
Jeff Schaller
-2

Todos los archivos en el directorio

grep 'search string' *

con recursivamente

grep -R 'search string' *
Markus
fuente
¿Quieres elaborar el -1?
Markus
44
No voté en contra, pero hay algunos problemas con los suyos: el OP mencionó una "lista de argumentos demasiado larga", que su primera no solucionará y probablemente sea lo que el OP estaba haciendo antes. El segundo tampoco ayuda en ese sentido (habría ayudado si lo hubieras usado en .lugar de *). *excluirá los archivos de puntos (aunque con -R, no los que están en los directorios recurrentes). -R en oposición a -r sigue enlaces simbólicos incluso con versiones recientes de GNU grep. También tendrá un problema con los archivos en el directorio actual cuyo nombre comienza con-
Stéphane Chazelas