¿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 unas 1000 líneas. Quiero la parte de mi archivo después de la línea que coincide con mi declaración grep.

Es decir,

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

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

¿Cómo lo hago ?

Author: aioobe, 2011-08-18

12 answers

Lo siguiente imprimirá la línea que coincida TERMINATE hasta el final del archivo:

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

Explicación: -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 TERMINATE (como grep) al final del archivo ($), y p es el comando print que imprime la línea actual.

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

sed -e '1,/TERMINATE/d'

Explicación: 1,/TERMINATE/ es una selección de rango de dirección (línea) que significa la primera línea para la entrada a la 1ª línea que coincide con la expresión regular TERMINATE, y d es el comando eliminar que elimina la línea actual y salta a la siguiente línea. Como sed el comportamiento predeterminado es imprimir las líneas, imprima las líneas después de TERMINATE hasta el 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 TERMINATE en 2 archivos diferentes en una sola pasada:

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

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

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

Edit2:

SI no desea codificar los nombres de archivo en el script sed, debe can:

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

Pero luego tienes que escapar de la $ que significa la última línea para que el shell no intente expandir la variable $w (tenga en cuenta que ahora usamos comillas dobles alrededor del script en lugar de comillas simples).

Olvidé decirle 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.


Editar: 2016-0530

Sébastien Clément preguntó: "¿Cómo reemplazarías el código duro TERMINATE por un variable?"

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

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

Para 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 acerca de reemplazar texto con variables en estos casos son: {[48]]}

  1. Variables ($variablename) encerradas en single quotes ['] no se" expandirá", pero las variables dentro double quotes ["] will. Por lo tanto, usted tiene que cambiar todo el single quotes a double quotes si contienen texto que desea reemplazar con una variable.
  2. Los rangos sed también contienen un $ y son inmediatamente seguidos por una letra como: $p, $d, $w. También se verán como variables para ser expandidas, por lo que tienes que escapar de esos $ caracteres con una barra invertida [\] como: \$p, \$d, \$w.
 245
Author: jfg956,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-04 01:18:22

Como una aproximación simple se podría utilizar

grep -A100000 TERMINATE file

Que greps para TERMINATE y salidas hasta 100000 líneas después de esa línea.

Desde la página de manual

-A NUM, --after-context=NUM

Imprima las líneas numéricas del contexto final después de las líneas coincidentes. Coloca una línea que contiene un separador de grupo ( -- ) entre grupos contiguos de partidos. Con la opción-o o matching only-matching opción, esto no tiene efecto y se da una advertencia.

 56
Author: aioobe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-08 12:57:20

Una herramienta para usar aquí es awk:

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

¿Cómo funciona esto:

  1. Establecemos la variable 'found' a cero, evaluando false
  2. si se encuentra una coincidencia para 'TERMINATE' con la expresión regular, la establecemos en uno.
  3. Si nuestra variable' found ' se evalúa como True, print:)

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

 24
Author: Jos De Graeve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-12 17:40:43

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

content=$(cat file)
echo "${content#*TERMINATE}"
 7
Author: Mu Qiao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-04 12:41:50

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

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

Explicación:

  1. Aunque no es una buena práctica, podría confiar en el hecho de que todos los vars tienen como valor predeterminado 0 o la cadena vacía si no está definida. Así que la primera expresión (if(found) print) no imprimirá nada para empezar.
  2. Después de terminar la impresión comprobamos si esta es la línea de inicio (que debería no incluido).

Esto imprimirá todas las líneas después de la línea TERMINATE.


Generalización:

  • Usted tiene un archivo con inicio y end-líneas y desea que las líneas entre las líneas excluyendo el inicio y end-líneas.
  • start - y end - las líneas podrían definirse mediante una expresión regular que coincida con la alinear.

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 end no se debe imprimir. Tenga en cuenta que esta comprobación se realiza antes de la impresión real para excluir la end-línea del resultado.
  2. Imprima la línea actual si found está activada.
  3. Si se encuentra la línea start, entonces 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 start-line del resultado.

Notas:

  • El código se basa en el hecho de que todos los awk-vars tienen por defecto 0 o la cadena vacía si no está definida. Esto es válido, pero puede no ser una buena práctica, por lo que podría agregar un BEGIN{found=0} al inicio de la expresión awk.
  • Si se encuentran múltiples bloques de inicio-fin, todos se imprimen.
 6
Author: UlfR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-29 09:22:55

Si por alguna razón, desea evitar usar sed, lo siguiente imprimirá la línea que coincida TERMINATE hasta el final del archivo:

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

Y lo siguiente se imprimirá desde la siguiente línea coincidente 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. Por otra parte, si el archivo dones no contiene TERMINATE, el 1er el comando falla.

 3
Author: jfg956,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-18 08:31:05

Hay muchas maneras 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 que desea comenzar a imprimir, puede especificarlo junto con NR (número de registro, que finalmente 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
 2
Author: fedorqui,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-31 10:40:19

Grep-A 10000000' TERMINATE ' file

  • es mucho, mucho más rápido que sed especialmente trabajando en archivos realmente grandes. Funciona hasta líneas de 10M (o lo que sea que pongas) por lo que no hay daño en hacer esto lo suficientemente grande como para manejar cualquier cosa que golpees.
 1
Author: user8910163,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-08 22:59:59

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

 0
Author: mivk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:30

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

Grep-A466' TERMINATE ' file

 0
Author: Mariah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-25 00:41:48

Sed es una herramienta mucho mejor para el trabajo: sed-n '/ re/, file p ' file

Donde re es regexp.

Otra opción es la opción gre after-context de grep. Es necesario pasar en un número para terminar en, utilizando wc en el archivo debe dar el valor correcto para detener en. Combine esto con-n y su expresión de coincidencia.

 -1
Author: ckwang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-18 07:09:33

Estos imprimirán todas las líneas desde la última línea encontrada "TERMINATE" 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
 -2
Author: easyyu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-13 21:52:15