¿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 perl
una 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 sed
solució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