Como grep -v e também excluir a próxima linha após a partida?


14

Como filtrar 2 linhas para cada linha que corresponde ao regex grep?
este é o meu teste mínimo:

SomeTestAAAA
EndTest
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestAABC
EndTest
SomeTestACDF
EndTest

E, obviamente, eu tentei, por exemplo, o grep -vA 1 SomeTestAAque não funciona.

a saída desejada é:

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

grep -v 'SomeTextAA' | uniq?
darkheart

Respostas:


14

Você pode usar grepcom -P(PCRE):

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

(?!AA)é o padrão de lookahead negativo de largura zero, garantindo que não haja AAdepois SomeTest.

Teste :

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

qual é o caractere de escape dos pontos? como Some.Test.AA?
Behrooz

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

Isso funciona nesse caso em particular, porque nos OPs as linhas de amostra vêm em pares SomeTest*\nEndTestpara que você efetivamente grepexecute ping em todas as linhas correspondentes, SomeTest*mas não SomeTestAA+ uma linha de contexto após a correspondência. Adicione mais algumas linhas à entrada (por exemplo, adicione uma linha foobarapós cada EndTestlinha) e tente novamente.
don_crissti

1
@don_crissti isso é verdade, eu já trabalhei nisso.
21815 Behrooz

@ Behrooz - gostaria de compartilhar conosco como você trabalhou em torno disso e talvez responder meu comentário em sua pergunta?
don_crissti

4

Aqui está uma sedsolução ( -nou seja, sem impressão automática) que funciona com entrada arbitrária:

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

então com uma entrada como

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

corrida

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

saídas

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

isto é, remove exatamente as linhas que grep -A1 SomeTestAA infileselecionariam:

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

Interessante. Não percebi que isso //correspondia /SomeTestAA/. Eu pensei que, neste caso, teria correspondido a expressão negada: /SomeTestAA/!. (+1)
Peter.O 30/08/2015

@ Peter.O - obrigado! Não, de acordo com as especificações, um ER vazio deve sempre corresponder ao último ER usado no último comando; o !não faz parte do ER , é uma sedcoisa.
don_crissti

3

Você pode ter melhor sorte com algo que considera as regiões com várias linhas como registros únicos. Há um sgrepque eu não usei muito.

Há também awk, onde você pode definir o separador de registros de entrada e o separador de registros de saída para o que quiser.

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

A maior parte do programa awk é de aspas simples, mas mudo para aspas duplas no final para que a $patvariável shell possa ser expandida.


awk -vpat="^SomeTestAA" -vRS="\nEndTest\n" 'BEGIN{ ORS=RS } $0 !~ pat' file
precisa saber é o seguinte

3

Uma opção é usar perl compatible regular expression grep:

pcregrep -Mv 'SomeTestAA.*\n' file

A opção -Mpermite que o padrão corresponda a mais de uma linha.


1
@don_crissti Ambas as linhas serão removidas. A especificação do OP não cobre este caso.
jimmij

É bastante óbvio que a amostra e a pergunta dos OPs não cobrem esses casos, estou curioso para saber como isso funciona (não estou familiarizado com o pcre) porque, com um número ímpar de linhas consecutivas correspondentes, isso funciona (ele remove a linha de contexto também) e com um número par de linhas consecutivas correspondentes, ela falha (não remove a linha de contexto depois).
don_crissti

Dado que (GNU) grepjá suporta PCRE (via -Popção), qual é a vantagem de usar pcregrep?
Arielf

@arielf grepnão suporta -Mopção.
jimmij 11/09/2015

1

Usando o padrão sed:

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

O sedscript analisa a linha de arquivo de entrada por linha, e quando uma linha corresponde ao padrão SomeTestAA, os dois sedcomandos de edição Ne dsão executados. O Ncomando anexa a próxima linha de entrada ao espaço do padrão (o buffer que sedpode editar) e dexclui o espaço do padrão e inicia o próximo ciclo.


1

Tentei com o comando Below sed e funcionou bem

comando

sed  '/SomeTestAA/,+1d' filename

resultado

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

0

Você pode usar sedo dcomando do GNU para excluir uma linha e prefixá-lo com /pat/,+Npara selecionar linhas que correspondam ao padrão e às N linhas subsequentes . No seu caso, N = 1, pois você deseja excluir a única linha subsequente após uma linha correspondente:

sed -e '/SomeTestAAAA/,+1d'
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.