bash - tutorial - ¿Cómo obtener la parte del archivo después de la línea que coincide con la expresión grep?(primer partido)
expresiones regulares tutorial (12)
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:
- Las variables (
$variablename
) entresingle quotes
[''
] no se "expandirán", pero las variables dentro dedouble quotes
["
] lo harán. Por lo tanto, debe cambiar todas lassingle quotes
double quotes
si contienen texto que desea reemplazar con una variable. - 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
.
Tengo un archivo con aproximadamente 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 que el archivo de la línea 535 to line 1000
para su posterior procesamiento.
Cómo lo hago ?
Alternativas a la excelente respuesta sed
de jfgagne, y que no incluyen la línea correspondiente:
-
awk ''/TERMINATE/ {y=1;next} y''
( https://.com/a/18166628 ) -
awk ''/TERMINATE/ ? c++ : c''
awk ''/TERMINATE/ ? c++ : c''
( https://.com/a/23984891 ) -
perl -ne ''print unless 1 .. /TERMINATE/''
( https://.com/a/18167194 )
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.
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''
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
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
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:
- 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. - 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:
- 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.
- Imprime la línea actual si está establecido.
- 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.
Una herramienta para usar aquí es awk:
cat file | awk ''BEGIN{ found=0} /TERMINATE/{found=1} {if (found) print }''
Como funciona esto:
- Establecemos la variable ''encontrado'' a cero, evaluando falsa
- si se encuentra una coincidencia para ''TERMINAR'' con la expresión regular, la configuramos en una.
- 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}"
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.
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.