Optimizando GNU grep

8

Estoy usando egrep ( grep -E) con un archivo PATTERN. ( -f path/to/file)

Esto se realiza en un bucle infinito en una secuencia de texto. Esto implica que no puedo acumular y pasar TODA la entrada a grep a la vez (como *.log).

¿Hay alguna manera de hacer que grep "guarde" el NFA que está construyendo desde el archivo PATTERN para usarlo en su próxima ejecución?

He buscado en Google y he leído la documentación sin suerte.

Trataré de explicarlo un poco más. Necesito localizar un número fijo de cadenas con expresiones regulares (esto no es parte de una pregunta, pero siéntase libre de sugerir lo contrario) como direcciones IP, dominios, etc. La búsqueda se realiza en un feed de Internet. Puedes pensarlo como una secuencia de texto. No puedo usar greptodas las entradas ya que es una secuencia. Puedo acumular un fragmento de flujo y usarlo grep(por lo tanto, no usarlo grepen cada línea), pero esto también es limitado (digamos durante 30 segundos).

Sé que grepestá construyendo un NFA a partir de todos sus patrones (en mi caso a partir de un archivo). Entonces mi pregunta aquí es: ¿puedo decirle grepque guarde esa NFA para la próxima ejecución, ya que no va a cambiar? Eso me ahorraría el tiempo de construir esa NFA cada vez.

bergerg
fuente
¿Qué quieres decir con esto se hace en un bucle infinito en una secuencia de texto ? ¿Estás diciendo que estás ejecutando uno greppor línea de texto? ¿De dónde viene el texto? ¿ tail -fSería una opción?
Stéphane Chazelas
Digamos que estoy acumulando la transmisión durante 30 segundos y luego ejecuto grepesa parte.
bergerg
1
Todavía no está claro por qué necesitarías correr grepvarias veces. Posiblemente relacionado: ¿Por qué es tan lento hacer coincidir 1250 cadenas con patrones de 90k?
Stéphane Chazelas
55
grepestá destinado a funcionar en una secuencia de texto, todavía no entiendo por qué necesitarías ejecutar varias instancias. ¿Por qué no puedes alimentar a todos a la misma grepinstancia? ¿Por qué necesitas acumularlos antes de alimentarlos grep?
Stéphane Chazelas
2
Eche un vistazo a flex y escriba su propio programa, que puede resultar mucho más rápido.
user2064000

Respuestas:

14

No, no hay tal cosa. En general, el costo de iniciar grep(bifurcar un nuevo proceso, cargar el ejecutable, la biblioteca compartida, la vinculación dinámica ...) sería mucho mayor que compilar las expresiones regulares, por lo que este tipo de optimización tendría poco sentido.

Sin embargo, vea ¿Por qué la coincidencia de 1250 cadenas con patrones de 90k es tan lenta? sobre un error en algunas versiones de GNU grepque lo haría particularmente lento para una gran cantidad de expresiones regulares.

Posiblemente aquí, podría evitar correr grepvarias veces alimentando sus fragmentos a la misma grepinstancia, por ejemplo, usándolo como un coproceso y use un marcador para detectar el final. Con zshy GNU grepe awkimplementaciones que no sean mawk:

coproc grep -E -f patterns -e '^@@MARKER@@$' --line-buffered
process_chunk() {
  { cat; echo @@MARKER@@; } >&p & awk '$0 == "@@MARKER@@"{exit};1' <&p
}
process_chunk < chunk1 > chunk1.grepped
process_chunk < chunk2 > chunk2.grepped

Aunque puede ser más simple hacer todo con awko en su perllugar.

Pero si no necesita la grepsalida para ir a diferentes archivos para diferentes fragmentos, siempre puede hacer:

{
  cat chunk1
  while wget -qO- ...; done # or whatever you use to fetch those chunks
  ...
} | grep -Ef patterns > output
Stéphane Chazelas
fuente
Tengo veraion 3+ de grep, así que ese no es el problema. Ni siquiera consideró la bifurcación en lo alto. Supongo que intentaré transmitir todo greptal como está. Gracias.
bergerg
¿El archivo ejecutable y las bibliotecas compartidas no permanecerían en los búferes de RAM después de la finalización de los procesos (a menos que el OP realmente tenga poca RAM)?
Dmitry Grigoryev
2
@DmitryGrigoryev, sí, lo más probable es que todavía necesite mapearse en el espacio de direcciones del proceso y editar el enlace. Hay más como cargar y analizar los datos locales, analizar las opciones, el entorno ... El punto es que el costo de regcomp () se diluye en toda esa sobrecarga. Lo primero que debe hacer al optimizar sería evitar ejecutar varios greps en primer lugar.
Stéphane Chazelas
1

No puedo usar grep en toda la entrada ya que es una secuencia. Puedo acumular un trozo de flujo y usar grep en él ...

¿Eres consciente de que las tuberías se bloquean? Si conecta algo a grep y toda la entrada no está disponible, grep esperará hasta que esté disponible y luego continuará como si la entrada estuviera allí todo el tiempo.

$ ( echo a1; echo b1; sleep 5; echo a2 ) | grep 'a.'
a1
a2

EDITAR: cómo funcionan las canalizaciones, por ejemplo, cmd1 | cmd2es que ambos programas se iniciarán al mismo tiempo, por ejemplo, con un "búfer de fragmentos" de 65.536 bytes entre ellos. Cuando cmd2intenta leer y ese búfer está vacío, esperará a que haya un fragmento disponible. Cuando cmd1intenta escribir y ese búfer está lleno, esperará hasta que lo cmd2lea.

Por lo que puedo leer, no hay necesidad de cortar la entrada en trozos y pasarlos a grep por separado. Eso ya está hecho automáticamente.

EDIT2: greptambién debe imprimir los resultados tan pronto como los encuentre en la secuencia. No es necesario que la transmisión finalice antes de que pueda obtener sus resultados.

JoL
fuente
0

¿Quizás pueda "usar grep en toda la entrada"? ¿Usando nc(netcat), o mediante script, o mediante otras herramientas similares? Especialmente si su archivo de patrones tiene un tamaño manejable (digamos menos de 1000 expresiones regulares).

Primer ejemplo : puede hacer egrepalguna conexión de transmisión: (aquí se muestra un ejemplo con nc, pero otros podrían aplicar)

prompt:/some/path $ nc somehost someport | egrep -f patternfile | gzip -c - > results.gz

# and while this is running, you can have a look at the growing results.gz:
prompt:/some/otherpath $ tail -f /some/path/results.gz | gzip -c - | less

(nota: incluso puede: touch /some/path/results.gzantes de iniciar el nccomando, y tener tail -fen ese archivo (vacío) para no perderse nada. De todos modos, los resultados.gz contendrán todo lo que quería atrapar)

segundo ejemplo : incluso podría egrepen una sesión de shell actualmente en ejecución (y mostrando otra forma de seguir la progresión):

#in 1 terminal:
prompt:/home/userA $ script
Script command is started. The file is typescript.
prompt:/home/userA $ 
 ... doing here whatever you want (start IRC? etc) ...
prompt:/home/userA $ ctrl-d # to end the current script session
Script command is complete. The file is typescript.

#and in another terminal, while you are "doing here whatever you want" :
prompt:/home/somewhere $ tail -f /home/userA/typescript | egrep -f patternfile  | tee /some/place/to/store/results.gz

egrepes una versión altamente eficiente de grep, en la mayoría de los sistemas (vea algunas informaciones interesantes en: https://swtch.com/~rsc/regexp/regexp1.html )

Olivier Dulac
fuente
incluso podría usar exemple1 en cosas como una salida de dd, etc.
Olivier Dulac
nota al margen interesante: grep es más eficiente cuanto más grande es la parte conocida de la expresión regular (por ejemplo: buscar la cadena o expresión regular ses mucho más lento que hacer coincidir somethingy esto es mucho más lento que hacer coincidir something even much longer(esto último permite que la coincidencia de expresión regular salte más grande) porciones de la entrada cuando difieren). En archivos grandes, básicamente "divide" el tiempo para analizarlo por la relación de longitud (es decir, agrupar 1 carácter conocido es casi 40 veces más lento que coincidir con una cadena de 40 caracteres conocidos. t prof it pero es realmente notable.)
Olivier Dulac