Solución:
Podrías usar gnu grep
con -A
y -B
para imprimir exactamente las partes del archivo que desea excluir, pero agregue el -n
cambie para imprimir también los números de línea y luego formatee la salida y páselo como un script de comando a sed
para borrar esas líneas:
grep -n -A1 -B2 PATTERN infile |
sed -n 's/^([0-9]{1,}).*/1d/p' |
sed -f - infile
Esto también debería funcionar con archivos de patrones pasados a grep
vía -f
p.ej:
grep -n -A1 -B2 -f patterns infile |
sed -n 's/^([0-9]{1,}).*/1d/p' |
sed -f - infile
Creo que esto podría optimizarse ligeramente si colapsara tres o más números de línea consecutivos en rangos para tener, por ejemplo, 2,6d
en lugar de 2d;3d;4d;5d;6d
… aunque si la entrada tiene solo unas pocas coincidencias, no vale la pena hacerlo.
Otras formas que no conservan el orden de las líneas y que probablemente sean más lentas:
con comm
:
comm -13 <(grep PATTERN -A1 -B2 <(nl -ba -nrz -s: infile) | sort)
<(nl -ba -nrz -s: infile | sort) | cut -d: -f2-
comm
requiere una entrada ordenada, lo que significa que el orden de las líneas no se conservará en la salida final (a menos que su archivo ya esté ordenado), por lo que nl
se utiliza para numerar las líneas antes de ordenar, comm -13
imprime solo líneas exclusivas de 2do ARCHIVO y luego cut
elimina la parte que fue agregada por nl
(es decir, el primer campo y el delimitador :
)
con join
:
join -t: -j1 -v1 <(nl -ba -nrz -s: infile | sort)
<(grep PATTERN -A1 -B2 <(nl -ba -nrz -s: infile) | sort) | cut -d: -f2-
don’s podría ser mejor en la mayoría de los casos, pero solo en caso de que el archivo sea De Verdad grande, y no puedes conseguir sed
para manejar un archivo de script tan grande (lo que puede suceder en alrededor de 5000+ líneas de script), aquí está con llano sed
:
sed -ne:t -e"/n.*$match/D"
-e'$!N;//D;/'"$match/{"
-e"s/n/&/$A;t"
-e'$q;bt' -e}
-e's/n/&/'"$B;tP"
-e'$!bt' -e:P -e'P;D'
Este es un ejemplo de lo que se llama ventana deslizante en la entrada. Funciona construyendo un mirar hacia el futuro búfer de $B
-contar líneas antes de intentar imprimir nada.
Y, de hecho, probablemente debería aclarar mi punto anterior: el limitador de rendimiento principal tanto para esta solución como para Don estará directamente relacionado con el intervalo. Esta solución se ralentizará con un intervalo mayor tamaños, mientras que los de Don se ralentizarán con un intervalo mayor frecuencias. En otras palabras, incluso si el archivo de entrada es muy grande, si la ocurrencia del intervalo real sigue siendo muy poco frecuente, entonces su solución probablemente sea el camino a seguir. Sin embargo, si el tamaño del intervalo es relativamente manejable y es probable que ocurra con frecuencia, esta es la solución que debe elegir.
Así que aquí está el flujo de trabajo:
- Si
$match
se encuentra en el espacio patrón precedido por unn
ewline,sed
será recursivamenteD
elegir cadan
ewline que lo precede.- Yo despejaba
$match
El espacio del patrón se ha eliminado completamente antes, pero para manejar fácilmente la superposición, dejar un punto de referencia parece funcionar mucho mejor. - Yo tambien probé
s/.*n.*($match)/1/
para intentar conseguirlo de una vez y esquivar el bucle, pero cuando$A/$B
son grandes, losD
elete loop resulta considerablemente más rápido.
- Yo despejaba
- Luego tiramos del
N
ext línea de entrada precedida por unn
ewline delimitador y vuelva a intentarD
elete un/n.*$match/
una vez más haciendo referencia a nuestra expresión regular utilizada más recientemente con//
. - Si el espacio del patrón coincide
$match
entonces solo puede hacerlo con$match
a la cabeza de la fila – todos$B
Antes de que se hayan despejado las líneas.- Entonces comenzamos a recorrer
$A
después. - Cada ejecución de este bucle intentaremos
s///
sustituto de&
en sí mismo el$A
thn
ewline en el espacio del patrón y, si tiene éxito,t
est nos ramificará – y todo nuestro$A
Después del búfer: fuera del script por completo para iniciar el script desde arriba con la siguiente línea de entrada, si la hubiera. - Si el
t
est no tiene éxito nosotrosb
rancho de regreso al:t
op etiqueta y recurse para otra línea de entrada, posiblemente comenzando el bucle si$match
ocurre mientras se reúne$A
después.
- Entonces comenzamos a recorrer
- Si pasamos un
$match
bucle de función, luego intentaremosp
rint el$
última línea si es así, y si!
no intentars///
sustituto de&
en sí mismo el$B
thn
carácter ewline en el espacio del patrón.- Bien
t
Esto también, y si tiene éxito nos ramificaremos a la:P
rint etiqueta. - Si no, volveremos a
:t
op y obtener otra línea de entrada adjunta al búfer.
- Bien
- Si llegamos a
:P
rint vamosP
rint entoncesD
elete hasta el primeron
ewline en el espacio del patrón y vuelva a ejecutar el script desde la parte superior con lo que queda.
Y esta vez, si estuviéramos haciendo A=2 B=2 match=5; seq 5 | sed...
El espacio del patrón para la primera iteración en :P
rint se vería así:
^1n2n3$
Y asi es como sed
reúne su $B
antes de búfer. Y entonces sed
imprime a la salida $B
-líneas de conteo detrás la entrada que ha recopilado. Esto significa que, dado nuestro ejemplo anterior, sed
haría P
rint 1
a la salida, y luego D
elija eso y envíe de vuelta a la parte superior del script un espacio de patrón que se parece a:
^2n3$
… y en la parte superior del guión el N
Se recupera la línea de entrada ext y, por lo tanto, la siguiente iteración se ve así:
^2n3n4$
Y así, cuando encontramos la primera aparición de 5
en la entrada, el espacio del patrón en realidad se ve así:
^3n4n5$
Entonces el D
El bucle elete se activa y cuando se completa se ve así:
^5$
Y cuando el N
se tira de la línea de entrada ext sed
golpea EOF y se cierra. Para ese momento solo ha P
líneas impresas 1 y 2.
Aquí hay un ejemplo de ejecución:
A=8 B=7 match="[24689]0"
seq 100 |
sed -ne:t -e"/n.*$match/D"
-e'$!N;//D;/'"$match/{"
-e"s/n/&/$A;t"
-e'$q;bt' -e}
-e's/n/&/'"$B;tP"
-e'$!bt' -e:P -e'P;D'
Eso imprime:
1
2
3
4
5
6
7
8
9
10
11
12
29
30
31
32
49
50
51
52
69
70
71
72
99
100
Si no te importa usar vim
:
$ export PAT=fff A=1 B=2
$ vim -Nes "+g/${PAT}/.-${B},.+${A}d" '+w !tee' '+q!' foo
aaa
bbb
ccc
hhh
iii
-
-Nes
activa el modo ex silencioso no compatible. Útil para la creación de scripts. -
+{command}
dile a vim que corra{command}
en el archivo. -
g/${PAT}/
– en todas las líneas coincidentes/fff/
. Esto se vuelve complicado si el patrón contiene caracteres especiales de expresión regular que no pretendía tratar de esa manera. -
.-${B}
– de 1 línea por encima de esta -
.+${A}
– a 2 líneas debajo de ésta (ver:he cmdline-ranges
para estos dos) -
d
– eliminar las líneas. -
+w !tee
luego escribe en la salida estándar. -
+q!
se cierra sin guardar los cambios.
Puede omitir las variables y usar el patrón y los números directamente. Los usé solo por claridad de propósito.