¿Cómo grep -v y también excluir la siguiente línea después del partido?


14

¿Cómo filtrar 2 líneas para cada línea que coincida con la expresión regular grep?
Esta es mi prueba mínima:

SomeTestAAAA
EndTest
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestAABC
EndTest
SomeTestACDF
EndTest

Y obviamente lo intenté, por ejemplo, grep -vA 1 SomeTestAAque no funciona.

la salida deseada es:

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

grep -v 'SomeTextAA' | uniq?
DarkHeart

Respuestas:


14

Puede usar grepcon -P(PCRE):

grep -P -A 1 'SomeTest(?!AA)' file.txt

(?!AA)es el patrón de búsqueda negativa negativo de ancho cero que garantiza que no haya un AAdespués SomeTest.

Prueba :

$ grep -P -A 1 'SomeTest(?!AA)' file.txt 
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

¿Cuál es el personaje de escape para los puntos? como Some.Test.AA?
Behrooz

1
@Behrooz Escape puntos por \.so grep -P -A 1 'SomeTest\.(?!AA)' file.txtogrep -P -A 1 'SomeTest(?!\.AA)' file.txt
heemayl

Esto funciona en este caso particular porque en los OP las líneas de muestra vienen en pares, por SomeTest*\nEndTestlo que actualmente está haciendo grepping a todas las líneas que coinciden SomeTest*pero no a SomeTestAA+ una línea de contexto después de la coincidencia. Agregue algunas líneas más a la entrada (por ejemplo, agregue una línea foobardespués de cada EndTestlínea) y luego vuelva a intentarlo.
don_crissti

1
@don_crissti es cierto, ya trabajé en eso.
Behrooz

@Behrooz: ¿te gustaría compartir con nosotros cómo trabajaste en eso y tal vez responder mi comentario bajo tu pregunta?
don_crissti

4

Aquí hay una sedsolución ( -nes decir, sin impresión automática) que funciona con entradas arbitrarias:

sed -n '/SomeTestAA/!p          # if line doesn't match, print it
: m                             # label m
//{                             # if line matches
$!{                             # and if it's not the last line
n                               # empty pattern space and read in the next line
b m                             # branch to label m (so n is repeated until a
}                               # line that's read in no longer matches) but
}                               # nothing is printed
' infile

así que con una entrada como

SomeTestAAXX
SomeTestAAYY
+ one line
SomeTestONE
Message body
EndTest
########
SomeTestTWO
something here
EndTest
SomeTestAABC
+ another line
SomeTestTHREE
EndTest
SomeTestAA
+ yet another line

corriendo

sed -n -e '/SomeTestAA/!p;: m' -e '//{' -e '$!{' -e 'n;b m' -e '}' -e'}' infile

salidas

SomeTestONE
Message body
EndTest
########
SomeTestTWO
something here
EndTest
SomeTestTHREE
EndTest

es decir, elimina exactamente las líneas que grep -A1 SomeTestAA infileseleccionarían:

SomeTestAAXX
SomeTestAAYY
+ one line
--
SomeTestAABC
+ another line
--
SomeTestAA
+ yet another line

Interesante. No me di cuenta de que //coincidía /SomeTestAA/. Pensé, en este caso, habría igualado la expresión negada: /SomeTestAA/!. (+1)
Peter.O

@ Peter.O - ¡gracias! No, según las especificaciones, un RE vacío siempre debe coincidir con el último RE utilizado en el último comando; El !no es parte del RE , es una sedcosa.
don_crissti

3

Es posible que tenga mejor suerte con algo que considera las regiones de varias líneas como registros únicos. Hay una sgrepque no he usado mucho.

También hay awk, donde puede configurar el separador de registros de entrada y el separador de registros de salida, a lo que desee.

pat="^SomeTestAA"
awk  'BEGIN{ RS=ORS="\nEndTest\n"} !/'"$pat/" foo

La mayor parte del programa awk está entre comillas simples, pero cambio a comillas dobles al final para que la $patvariable de shell se pueda expandir.


awk -vpat="^SomeTestAA" -vRS="\nEndTest\n" 'BEGIN{ ORS=RS } $0 !~ pat' file
Peter.O

3

Una opción es utilizar perl compatible regular expression grep:

pcregrep -Mv 'SomeTestAA.*\n' file

La opción -Mpermite que el patrón coincida con más de una línea.


1
@don_crissti Ambas líneas serán eliminadas. La especificación de OP no cubre este caso.
jimmij

Es bastante obvio que la muestra y la pregunta de los OP no cubren tales casos, solo tengo curiosidad por saber cómo funciona esto (no estoy familiarizado con pcre) porque con un número impar de líneas consecutivas que coinciden, esto funciona (elimina la línea de contexto también) y con un número par de líneas consecutivas que coinciden, falla (no elimina la línea de contexto después).
don_crissti

Dado que (GNU) grepya es compatible con PCRE (a través de la -Popción), ¿cuál es la ventaja de usar pcregrep?
arielf

@arielf grepno admite la -Mopción.
jimmij

1

Usando estándar sed:

$ sed '/SomeTestAA/{ N; d; }' file
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

La sedsecuencia de comandos analiza la línea de archivo de entrada por línea, y cuando una línea coincide con el patrón SomeTestAA, los dos sedcomandos de edición Ny dse ejecutan. El Ncomando agrega la siguiente línea de entrada al espacio del patrón (el búfer que sedpuede editar), y delimina el espacio del patrón y comienza el siguiente ciclo.


1

Intenté con el comando Abajo sed y funcionó bien

mando

sed  '/SomeTestAA/,+1d' filename

salida

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

0

Puede usar sedel dcomando de GNU para eliminar una línea y agregarle un prefijo /pat/,+Npara seleccionar líneas que coincidan con el patrón y las N líneas subsiguientes . En su caso, N = 1 ya que solo desea eliminar la única línea posterior después de una línea coincidente:

sed -e '/SomeTestAAAA/,+1d'
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.