¿Cómo obtener la parte del archivo después de la línea que coincide con la expresión grep? (primer partido)

Tengo un archivo con aproximadamente 1000 líneas. Quiero la parte de mi archivo después de la línea que coincide con mi statement grep.

es decir

$ cat file | grep 'TERMINATE' // Its found on line 534 

Por lo tanto, quiero que el archivo de la línea 535 to line 1000 para su posterior procesamiento.

Cómo lo hago ?

A continuación, se imprimirá la línea que coincide TERMINATE hasta el final del archivo:

 sed -n -e '/TERMINATE/,$p' 

Explicado: -n desactiva el comportamiento predeterminado de sed de imprimir cada línea después de ejecutar su script en él, -e indicó un script para sed , /TERMINATE/,$ es una selección de rango de dirección (línea) que significa la primera línea que coincide con la expresión regular TERMINAR (como grep) hasta el final del archivo ( $ ), y p es el comando de impresión que imprime la línea actual.

Esto se imprimirá desde la línea que sigue a la línea que coincide TERMINATE hasta el final del archivo:
(desde DESPUÉS de la línea coincidente con EOF, NO incluyendo la línea correspondiente)

 sed -e '1,/TERMINATE/d' 

Explicado: 1,/TERMINATE/ es una selección de rango de dirección (línea) que significa la primera línea para la entrada a la 1ra línea que coincide con la expresión regular TERMINAR, y d es el comando de borrar que borra la línea actual y salta a la próxima línea. Como el comportamiento predeterminado de sed es imprimir las líneas, imprimirá las líneas después de TERMINATE al final de la entrada.

Editar:

Si quieres las líneas antes de TERMINATE :

 sed -e '/TERMINATE/,$d' 

Y si desea ambas líneas antes y después de TERMINATE en 2 archivos diferentes en una sola pasada:

 sed -e '1,/TERMINATE/w before /TERMINATE/,$w after' file 

Los archivos de antes y después contendrán la línea con terminación, por lo tanto, para procesar cada uno, debe usar:

 head -n -1 before tail -n +2 after 

Edit2:

SI no desea codificar los nombres de los archivos en el script sed, puede:

 before=before.txt after=after.txt sed -e "1,/TERMINATE/w $before /TERMINATE/,\$w $after" file 

Pero luego debe escapar de $ significa la última línea para que el intérprete de comandos no intente expandir la variable $w (tenga en cuenta que ahora usamos comillas dobles alrededor del guión en lugar de comillas simples).

Olvidé decir que la nueva línea es importante después de los nombres de archivo en el script para que sed sepa que los nombres de archivo terminan.

Edición: 2016-0530

Sébastien Clément preguntó: “¿Cómo reemplazarías el TERMINADO codificado por una variable?”

Haría una variable para el texto coincidente y luego lo haría de la misma manera que en el ejemplo anterior:

 matchtext=TERMINATE before=before.txt after=after.txt sed -e "1,/$matchtext/w $before /$matchtext/,\$w $after" file 

usar una variable para el texto coincidente con los ejemplos anteriores:

 ## Print the line containing the matching text, till the end of the file: ## (from the matching line to EOF, including the matching line) matchtext=TERMINATE sed -n -e "/$matchtext/,\$p" 
 ## Print from the line that follows the line containing the ## matching text, till the end of the file: ## (from AFTER the matching line to EOF, NOT including the matching line) matchtext=TERMINATE sed -e "1,/$matchtext/d" 
 ## Print all the lines before the line containing the matching text: ## (from line-1 to BEFORE the matching line, NOT including the matching line) matchtext=TERMINATE sed -e "/$matchtext/,\$d" 

Los puntos importantes sobre la sustitución de texto con variables en estos casos son:

  1. Las variables ( $variablename ) entre single quotes [ ' ] no se “expandirán”, pero las variables dentro de double quotes [ " ] lo harán. Por lo tanto, debe cambiar todas las single quotes double quotes si contienen texto que desea reemplazar con una variable.
  2. Los rangos de sed también contienen un $ e inmediatamente son seguidos por una letra como: $p , $d , $w . También se verán como variables a expandir, por lo que debe escapar esos $ caracteres con una barra invertida [ \ ] como: \$p , \$d , \$w .

Como una aproximación simple, podrías usar

 grep -A100000 TERMINATE file 

que greps para TERMINATE y saca hasta 100000 líneas siguiendo esa línea.

De la página de manual

-A NUM, --after-context=NUM

Imprima NUM líneas de contexto posterior después de hacer coincidir líneas. Coloca una línea que contiene un separador de grupo (-) entre grupos contiguos de coincidencias. Con la opción -o-solo-matching, esto no tiene ningún efecto y se da una advertencia.

Una herramienta para usar aquí es awk:

 cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1} {if (found) print }' 

Como funciona esto:

  1. Establecemos la variable ‘encontrado’ a cero, evaluando falsa
  2. si se encuentra una coincidencia para ‘TERMINAR’ con la expresión regular, la configuramos en una.
  3. Si nuestra variable ‘found’ se evalúa como True, imprime 🙂

Las otras soluciones pueden consumir mucha memoria si las usa en archivos muy grandes.

Use la expansión del parámetro bash como la siguiente:

 content=$(cat file) echo "${content#*TERMINATE}" 

Si entiendo su pregunta correctamente, quiere las líneas después de TERMINATE , sin incluir la línea TERMINAR. awk puede hacer esto de una manera simple:

 awk '{if(found) print} /TERMINATE/{found=1}' your_file 

Explicación:

  1. Aunque no es la mejor práctica, puedes confiar en el hecho de que todos los valores predeterminados de vars a 0 o la cadena vacía si no están definidos. Entonces, la primera expresión ( if(found) print ) no imprimirá nada para comenzar.
  2. Una vez realizada la impresión, verificamos si esta es la línea de inicio (que no debe incluirse).

Esto imprimirá todas las líneas después de TERMINATE -line.


Generalización:

  • Tiene un archivo con líneas de inicio y final y quiere las líneas entre esas líneas excluyendo el inicio y las líneas finales .
  • Las líneas de inicio y fin se pueden definir con una expresión regular que coincida con la línea.

Ejemplo:

 $ cat ex_file.txt not this line second line START A good line to include And this line Yep END Nope more ... never ever $ awk '/END/{found=0} {if(found) print} /START/{found=1}' ex_file.txt A good line to include And this line Yep $ 

Explicación:

  1. Si se encuentra la línea final, no se debe realizar ninguna impresión. Tenga en cuenta que esta comprobación se realiza antes de la impresión real para excluir la línea final del resultado.
  2. Imprime la línea actual si está establecido.
  3. Si se encuentra la línea de inicio , establezca found=1 para que se impriman las siguientes líneas. Tenga en cuenta que esta comprobación se realiza después de la impresión real para excluir la línea de inicio del resultado.

Notas:

  • El código se basa en el hecho de que todos los awk-vars por defecto son 0 o la cadena vacía si no está definida. Esto es válido, pero puede no ser la mejor práctica, así que podría agregar un BEGIN{found=0} al comienzo de la expresión awk.
  • Si se encuentran múltiples bloques de inicio y fin , todos se imprimen.

Si por algún motivo, desea evitar el uso de sed, lo siguiente imprimirá la línea correspondiente TERMINATE hasta el final del archivo:

 tail -n "+$(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)" file 

y lo siguiente imprimirá desde la siguiente línea que coincide TERMINATE hasta el final del archivo:

 tail -n "+$(($(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)+1))" file 

Se necesitan 2 procesos para hacer lo que sed puede hacer en un proceso, y si el archivo cambia entre la ejecución de grep y tail, el resultado puede ser incoherente, por lo que recomiendo usar sed. Además, si el archivo no contiene TERMINATE , el primer comando falla.

Hay muchas formas de hacerlo con sed o awk :

 sed -n '/TERMINATE/,$p' file 

Esto busca TERMINATE en su archivo e imprime desde esa línea hasta el final del archivo.

 awk '/TERMINATE/,0' file 

Este es exactamente el mismo comportamiento que sed .

En caso de que conozca el número de la línea desde la cual desea comenzar a imprimir, puede especificarlo junto con NR (número de registro, que eventualmente indica el número de la línea):

 awk 'NR>=535' file 

Ejemplo

 $ seq 10 > a #generate a file with one number per line, from 1 to 10 $ sed -n '/7/,$p' a 7 8 9 10 $ awk '/7/,0' a 7 8 9 10 $ awk 'NR>=7' a 7 8 9 10 

grep -A 10000000 archivo ‘TERMINAR’

  • es mucho, mucho más rápido que sed, trabajando especialmente en archivos realmente grandes. Funciona hasta 10 millones de líneas (o lo que sea que coloques) por lo que no hay daño en hacer que este sea lo suficientemente grande como para manejar cualquier cosa que golpees.

Alternativas a la excelente respuesta sed de jfgagne, y que no incluyen la línea correspondiente:

Esta podría ser una forma de hacerlo. Si sabe en qué línea del archivo tiene su palabra grep y cuántas líneas tiene en su archivo:

grep -A466 archivo ‘TERMINAR’

sed es una herramienta mucho mejor para el trabajo: archivo sed -n ‘/ re /, $ p’

donde re es regexp.

Otra opción es la bandera de grep – after-context. Necesita pasar un número para finalizar en, usar wc en el archivo debe dar el valor correcto para detenerse. Combina esto con -n y tu expresión de coincidencia.

Estos imprimirán todas las líneas de la última línea encontrada “TERMINAR” hasta el final del archivo:

 LINE_NUMBER=`grep -o -n TERMINATE $OSCAM_LOG|tail -n 1|sed "s/:/ \\'/g"|awk -F" " '{print $1}'` tail -n +$LINE_NUMBER $YOUR_FILE_NAME