Limite la salida de grep a líneas cortas

8

A menudo uso grep para encontrar archivos que tengan una determinada entrada como esta:

grep -R 'MyClassName'

Lo bueno es que devuelve los archivos, su contenido y marca la cadena encontrada en rojo. Lo malo es que también tengo archivos enormes donde todo el texto está escrito en una sola línea grande. Ahora grep genera demasiados resultados al encontrar texto dentro de esos archivos grandes. ¿Hay alguna forma de limitar la salida a, por ejemplo, 5 palabras a la izquierda y a la derecha? ¿O tal vez limite la salida a 30 letras a la izquierda y a la derecha?

Sócrates
fuente
3
cut
Canalice
Entonces, digamos que el patrón que estás buscando está en la posición 50, pero dijiste que solo quieres 30 letras. ¿Qué quieres hacer entonces? ¿Ignora esa línea o también la incluye en la salida pero la recorta? ¿Qué es exactamente lo que quiere limitar: la búsqueda o las líneas mismas?
Sergiy Kolodyazhnyy
1
@Rinzwind No entiendo muy bien con qué quieres lograr cut, ya que solo se divide por delimitador o por recuento de caracteres. Aunque cuando encuentro una línea con MyClassNameella, puede estar en cualquier parte de la línea y no siempre en la misma posición. Además, puede haber una variación de caracteres en la parte delantera y trasera de la misma, lo que rompe la posibilidad de dividir por delimitador.
Sócrates
1
@SergiyKolodyazhnyy Cuando MyClassNamese ha encontrado una línea positiva con , quiero obtener como resultado el nombre del archivo y los caracteres x a la izquierda y a la derecha. x es cualquier número que proporcione, por ejemplo 30. El resto del contenido del archivo se ignorará. Esto es para obtener un contexto para los archivos coincidentes y limitar la sobrecarga.
Sócrates
1
@Rinzwind ¿Con qué tipo de delimitador personalizado sugeriría cutsi hay tres archivos con la siguiente entrada: oiadfaosuoianavMyClassNameionaernaldfajdy /(/&%%§%/(§(/MyClassName&((/$/$/(§/$&y public class MyClassName { public static void main(String[] args) { } }?
Sócrates

Respuestas:

15

grepsolo tiene opciones de contexto basadas en líneas. Esta publicación SU sugiere una alternativa :

Una solución alternativa es habilitar la opción 'solo coincidencia' y luego usar el poder de RegExp para grep un poco más que su texto:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}" ./filepath

Por supuesto, si usa el resaltado de color, siempre puede volver a grep para colorear solo la combinación real:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}"  ./filepath | grep "WHAT_I_M_SEARCHING"

Como otra alternativa, sugeriría foldel texto y luego lo grepé, por ejemplo:

fold -sw 80 input.txt | grep ...

La -sopción hará que foldlas palabras de inserción pasen a la siguiente línea en lugar de dividirse en el medio.

O utilice alguna otra forma de dividir la entrada en líneas según la estructura de su entrada. (La publicación SU, por ejemplo, se ocupó de JSON, por lo que usar jqetc. para imprimir bonitas y grep... o simplemente usar jqpara hacer el filtrado por sí mismo ... sería mejor que cualquiera de las dos alternativas dadas anteriormente).


Este método GNU awk podría ser más rápido:

gawk -v n=50 -v RS='MyClassName' '
  FNR > 1 { printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)}
  {p = substr($0, length - n); prt = RT}
' input.txt
  • Dile a awk que divida los registros en el patrón que nos interesa ( -v RS=...) y la cantidad de caracteres en contexto ( -v n=...)
  • Cada registro después del primer registro ( FNR > 1) es uno donde awk encontró una coincidencia para el patrón.
  • Entonces imprimimos nlos caracteres finales de la línea anterior ( p) y nlos caracteres iniciales de la línea actual ( substr($0, 0, n)), junto con el texto coincidente de la línea anterior (que es prt)
    • establecemos py prt después de imprimir, entonces el valor que establecemos es usado por la siguiente línea
    • RT es un GNUismo, es por eso que es GNU awk-específico.

Para la búsqueda recursiva, tal vez:

find . -type f -exec gawk -v n=50 -v RS='MyClassName' 'FNR>1{printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)} {p = substr($0, length-n); prt = RT}' {} +
muru
fuente
2
Ok, funciona Parece que Regex es un enfoque válido, así que gracias por eso. Sin embargo, el tiempo de procesamiento es bastante grande. Sin Regex como en mi publicación anterior, toma 4.912s y con Regex como en tu publicación toma 3m39.312s.
Sócrates
1
@Socrates ve si el método awk que agregué anteriormente funciona mejor
muru
1
El foldmétodo solo se puede usar si está seguro de que la cadena buscada no aparece en el borde, de lo contrario se ocultaría grep.
Melebius
1
@muru Gracias por su sugerencia con gawk. Desafortunadamente, el comando sugerido con findresultados aleatorios y sin nombres de archivo, cuando se ejecuta en mi sistema. Además, no soy lo suficientemente fluido como awkpara analizar adecuadamente el comando. Actualmente, Regex en combinación con grepresuelve el problema tal vez no sea rápido, pero confiable. De nuevo muchas gracias.
Sócrates
1
@ Socrates Creo que logré arreglar el comando awk. Mi modelo mental estaba equivocado acerca de qué línea RTy prefijo, etc. debían usarse.
muru
1

El uso de solo coincidencia en combinación con algunas otras opciones (ver más abajo), podría estar muy cerca de lo que está buscando, sin la sobrecarga de procesamiento de expresiones regulares mencionada en la otra respuesta

grep -RnHo 'MyClassName'
  • n salida numérica, muestra el número de línea de la coincidencia
  • H nombre de archivo, muestra el nombre de archivo al comienzo de la línea de la coincidencia
  • o solo coincide, solo muestra la cadena con matices, no toda la línea
Robert Riedl
fuente
Si bien es cierto que el resultado se encuentra mucho más rápido, falta información. Se muestra la ruta del archivo, se muestra el número de línea, pero la salida de texto es solo mi búsqueda inicial MyClassName. Por lo tanto, falta el contexto.
Sócrates
grep -RnHo "MyClassName"y grep -Rno "MyClassName"tienen la misma salida.
Sócrates
La salida de @Socrates no es la misma sin H en el mismo directorio
Robert Riedl
La -obandera podría ser interesante si la expresión regular tuviera alguna parte variable. Para una cadena fija, es inútil imprimirla cada vez. Lo más probable es que OP esté interesado en el contexto cercano.
Melebius
1
@Socrates, cierto: falta el contexto, pero pensé que ese era el punto Limitar la salida? Puede agregar contexto nuevamente agregando las líneas antes ( -B 1) o después ( -A 1). Lamento no poder ser de más ayuda.
Robert Riedl