grep para devolver las líneas Nth y Mth antes y después del partido

12

Sé que con grep puedo usar los campos -Ay -Bextraer líneas anteriores y siguientes de un partido.

Sin embargo, tiran de todas las líneas entre la coincidencia en función de la cantidad de líneas especificadas.

grep -r -i -B 5 -A 5 "match" 

Me gustaría recibir solo la línea antes de un partido y la línea después del partido además de la línea coincidente y no obtener las líneas intermedias.

¿Hay alguna manera de hacer esto con el grep?

chollida
fuente
1
Podrías hacerlo poniéndolo en sed. Acabo de probar esto y funcionó, pero solo funcionó cuando había 1 coincidencia exacta en el archivo: grep -r -i -B 5 -A 5 "match" | sed -e 1b -e '$!d'
Terrance
@Terrance gracias por la sugerencia, como mencionas, ya que estoy recolectando miles de líneas, esto no funcionará.
chollida
No creo que grep funcione por sí solo ... Estoy trabajando en un script de bash para ti
Joshua Besneatte
¡No hay problema! Un poco interesado en ver qué respuestas obtienes. =)
Terrance
¿Está esto en un archivo o en varios archivos?
Joshua Besneatte

Respuestas:

1

La herramienta que desea usar se llama tamizar. Esto es básicamente un grep en esteroides. Grep en paralelo. Sift tiene una gran cantidad de opciones para hacer exactamente lo que desea, específicamente para devolver una línea particular en relación con una (s) coincidencia (s) que puede / no puede ser seguida / precedida por algún texto.

Me sorprende que sift no sea un gnu convencional, ya que fue escrito en el lenguaje go, pero se instala en Linux perfectamente. IT busca en paralelo utilizando todas las grandes cantidades de texto de cpus donde grep solo lleva semanas para hacer lo mismo.

Sitio web de Sift - ver ejemplos

Brandon Haberfeld
fuente
Bienvenido a AskUbuntu, gracias por responder. Debe proporcionar un ejemplo de CLI que pueda resolver este problema específico en lugar de proporcionar un enlace para filtrar el sitio web. Esto es un Q&A después de todo, gracias.
Bernard Wei
12

Si:

cat file
a
b
c
d
e
f match
g
h
i match
j
k
l
m
n
o

Entonces:

awk '
    {line[NR] = $0} 
    /match/ {matched[NR]} 
    END {
        for (nr in matched)
            for (n=nr-5; n<=nr+5; n+=5) 
                print line[n]
    }
' file
a
f match
k
d
i match
n
Glenn Jackman
fuente
+1, pero ¿podrías explicar la semántica de /match/ {matched[NR]}? Nunca he visto una matriz o variable como un comando completo. ¿Está poniendo el número de registro actual de cada línea coincidente en la matriz.
Joe
Esta es una extraña rareza: si hace referencia a un elemento de matriz sin asignación, esa clave se agrega a la matriz (sin un valor). Entonces esa clave aparece en la expresión key in array. Lo que estoy haciendo es recordar los números de línea donde aparece el patrón
glenn jackman
6

Esta es básicamente la solución de Glenn, pero implementada con Bash, Grep y sed.

grep -n match file |
    while IFS=: read nr _; do
        sed -ns "$((nr-5))p; $((nr))p; $((nr+5))p" file
    done

Tenga en cuenta que los números de línea menores que 1 generarán un error de sed, y los números de línea mayores que el número de líneas en el archivo harán que no imprima nada.

Esto es solo el mínimo indispensable. Hacer que funcione de manera recursiva y manejar los casos de números de línea anteriores requeriría algo de trabajo.

wjandrea
fuente
6

No se puede hacer solo con eso grep. Si edes una opción:

ed -s file << 'EOF' 
g/match/-5p\
+5p\
+5p
EOF  

El script básicamente dice: por cada coincidencia de / match /, imprima la línea 5 líneas antes de eso, luego 5 líneas después de eso, luego 5 líneas después de eso.

JoL
fuente
55
@ubashu ¿Crees que será más útil para el OP dar un simple "no se puede hacer con grep"? Proporciono lo que creo que es una buena alternativa para resolver el problema de OP. Desde el Centro de ayuda: "¿Qué es, específicamente, la pregunta que se hace? Asegúrese de que su respuesta proporcione eso, o una alternativa viable. La respuesta puede ser 'no haga eso', pero también debe incluir 'intente esto en su lugar' ".
JoL
edsiempre es una respuesta, porque edes el editor de texto estándar.
postre
55
@ubashu Aunque no es una greprespuesta, la respuesta de "No puedes hacerlo con X, pero puedes hacerlo con Y, así es cómo" sigue siendo una respuesta válida ya que no solo respondes la pregunta de OP sino que también ofreces una alternativa eso funcionaria. Este es un tipo válido de respuesta aquí.
Thomas Ward
5
awk '/match/{system("sed -n \"" NR-5 "p;" NR "p;" NR+5 "p\" " FILENAME)}' infile

Aquí estamos usando la función de awk para llamar a un comando externo para imprimir las líneas que awk coinciden con el patrón con las líneas antes y después del partido.system(command)sedmatch

La sintaxis es fácil, solo necesita colocar el comando externo dentro de comillas dobles y sus interruptores y escapar de las cosas que desea pasar exactamente al comando, todo lo demás relacionado con las awkopciones en sí debe estar fuera de las comillas. Entonces el siguiente sed :

"sed -n \"" NR-5 "p;" NR "p;" NR+5 "p\" " FILENAME

traducir en:

sed -n "NR-5p; NRp; NR+5p" FILENAME

NRes el número de línea que coincide con el patrón matchy FILENAMEes el nombre de archivo de procesamiento actual que pasa awk.

αғsнιη
fuente
2

usando el archivo de texto de ejemplo de @ glenn y usando perl en lugar de awk:

$ perl -n0E 'say /(.*\n)(?=(?:.*\n){4}(.*match.*\n)(?:.*\n){4}(.*\n))/g' ex

dará los mismos resultados, pero se ejecutará más rápido:

a
f match
k
d
i match
n
Fabby
fuente
João, estás apareciendo en la cola de revisión de LQ y @waltinator votó para eliminar, así que la próxima vez sé un poco más detallado ... ;-) También +1 para sacarte de la cola de LQ ... : P
Fabby
1
@JJoao Cola de revisión de baja calidad. Su respuesta probablemente fue recogida allí porque era 90% de código.
wjandrea
1
@JJoao La cifra del 90% es solo mi forma de explicarlo. No sé qué heurísticas se usan realmente.
wjandrea
1
Menos café, más escrita! @JJoao : D ;-): D
Fabby
1
@Fabby: Sem café nada funciona: D - probablemente aparecería en la LCQ (= cola de café baja)