Extraiga parte del archivo de texto desde la primera aparición de una cadena hasta la primera aparición de otra

Extraiga parte del archivo de texto desde la primera aparición de una cadena hasta la primera aparición de otra

¿Cómo puedo extraer una parte de un archivo de texto grande, comenzando con la primera aparición de FOO y terminando con la primera aparición de BAR?

En mi caso, estoy intentando extraer una parte de un archivo SQL creado por mysqldump.

Respuesta1

Créditos para@dgigy@Paulo¡Quienes me ayudaron con sus comentarios!Final perluna sola línea aquí:

perl -lne 'if(/FOO/../BAR/){s/.*?(FOO)/$1/ if!$i++;s/BAR\K.*//&&print&&exit;print}' file

Explicación:

if(/FOO/../BAR/){        # perform the following actions on each line, starting
                         # with a line that contains FOO, and up to and including
                         # a line that contains BAR  
s/.*?(FOO)/$1/ if!$i++;  # only on the first line that contains FOO,
                         # delete all characters before FOO  
s/BAR\K.*//&&print&&exit;# if the line contains BAR, remove characters
                         # after BAR, print the line and stop processing  
print                    # simply print the line contents

Antigua respuesta:

Créditos para@Paulopara una sedsolución sencilla. Es igual de simple y fácil de leer en awk:

awk '/FOO/,/BAR/' file

Sin embargo, podría ser demasiado simple: devuelve líneas completas y no exactamente "una porción de texto que comienza en la primera aparición de FOO y termina en la primera aparición de BAR". Creo que eso significa que FOO debería ser la primera palabra y BAR la última. Hacer exactamente eso requiere una respuesta más complicada. Déjame intentar lograrlo en perl.

Caso simple (devuelve líneas completas):

perl -lne 'print if /FOO/../BAR/' file

Caso complejo (exactamente de FOO a BAR):

perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//;print}' file

Me gusta esta solución equivalente, que asigna una variable al operador de rango:

perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*// if$a=~/E/;print}' file

Nota:Se supone que sólo hay una porción de texto a extraer, es decir, no deberíamos encontrar otro FOO después del primer párrafo delimitado por FOO y BAR.

De lo contrario, el caso simple ya no lo es tanto en awk:

awk '/FOO/,/BAR/ {print; if ($0~/BAR/) {exit} }' file

y en perl:

perl -lne '(print&&/BAR/&&exit) if /FOO/../BAR/' file

Y las soluciones complejas y más refinadas se convierten en:

perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//&&print&&exit;print}' file

y:

perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*//&&print&&exit if$a=~/E/;print}' file

Este ejemplo muestra cómo una frase breve puede pasar de ser excepcionalmente clara y autoexplicativa a lo que parece una secuencia oscura de caracteres aleatorios, por haber agregado un poco más de complejidad al problema. Siempre que sea necesario, recomendaría escribir un script independiente, mantenible y legible donde se puedan agregar fácilmente funciones adicionales y tener en cuenta los casos extremos.

Respuesta2

En este caso no fue tan difícil como pensé que podría ser. Con sed, desde la primera aparición de FOO hasta la primera aparición de BAR (no lo intenté, pero probablemente algo como el segundo FOO al segundo BAR sería más difícil).

sed -nr '/FOO/ {
/FOO/ s/[^F]+FOO/FOO/p
:a
n
/BAR/ s/([^B]+BAR).*/\1/
p
/BAR/ q
ba
}' <<<'line1
> line2 FOO text1 FOO text2
> line3
> line4 BAR text3 BAR text4
> line5'

FOO text1 FOO text2
line3
line4 BAR

información relacionada