Remova a linha que contém determinada string e a seguinte linha

71

Eu uso isso

cat foo.txt | sed '/bar/d'

para remover linhas que contêm a sequência barno arquivo.

Gostaria, no entanto, de remover essas linhas e a linha logo após . De preferência em sed, awkou outra ferramenta disponível no MinGW32.

É uma espécie de reverso do que eu posso entrar em grepcom -Ae -Bpara imprimir linhas correspondentes, bem como linhas antes / depois da linha correspondido.

Existe alguma maneira fácil de conseguir isso?

jakub.g
fonte
2
Apenas para informação: estou analisando logs nos quais as entradas são de duas linhas. Então, eu quero encontrar uma entrada que corresponda ao padrão e removê-lo, assim como a próxima linha. Por isso, não preciso lidar com linhas de partidas consecutivas, mas, de qualquer forma, obrigado pela conclusão de suas respostas!
jakub.g

Respostas:

75

Se você possui o GNU sed (Linux ou Cygwin não incorporado):

sed '/bar/,+1 d'

Se você tiver barduas linhas consecutivas, isso excluirá a segunda linha sem analisá-la. Por exemplo, se você tiver um arquivo de 3 linhas bar/ bar/ foo, a foolinha permanecerá.

Gilles 'SO- parar de ser mau'
fonte
11
+1 para o comprimento :) No meu exemplo em particular, não tenho bars consecutivos, portanto, este é super fácil de lembrar.
jakub.g
11
sed '/bar/d'se você quiser apenas "Remover linha contendo determinada string" e não a próxima.
AJP
Se eu quiser remover todas as linhas depois da matemática, então?
Pandya
11
@ Pandya Isso é diferente. Você pode usar, por exemplo,sed '/math/q'
Gilles 'SO- stop be evil'
11
@AK Se você quiser apenas excluir a linha correspondente, é ainda mais simples:sed '/bar/d'
Gilles 'SO- stop be evil'
16

Se barocorrer em linhas consecutivas, você poderá:

awk '/bar/{n=2}; n {n--; next}; 1' < infile > outfile

que pode ser adaptado para excluir mais de 2 linhas, alterando as 2 acima com o número de linhas a serem excluídas, incluindo a correspondente.

Caso contrário, é fácil concluir sedcom a solução @MichaelRollins ou:

sed '/bar/,/^/d' < infile > outfile
Stéphane Chazelas
fonte
A outra vantagem na solução AWK é que eu posso substituir /bar/com /bar|baz|whatever/. Nessa sedsintaxe parece não funcionar.
jakub.g
@ jakub.g, eu tenho o GNU sed (v4.4 agora). Não tenho certeza sobre os outros. O que eu sei é que ele usa sintaxe de expressão regular "básica" por padrão, é por isso que seu exemplo não funcionou. Para conseguir o que você deseja, você pode colocar uma barra invertida na frente de cada linha vertical ou pedir sedpara usar expressões regulares "estendidas". Mais informações aqui: gnu.org/software/sed/manual/html_node/… . Observe que isso também é aplicável grep. Aqui está o meu próprio exemplo de trabalho: echo $'0a\n1b\n2c' | sed '/0a\|1b/d'.
Victor Yarema 14/07
12

Não sou fluente no sed, mas é fácil fazê-lo no awk:

awk '/bar/{getline;next} 1' foo.txt 

O script awk diz: para uma linha que contém bar, pegue a próxima linha (getline) e, em seguida, pule todo o processamento subsequente (próximo). O padrão 1 no final imprime as linhas restantes.

Atualizar

Como apontado no comentário, a solução acima não funcionou com consecutivas bar. Aqui está uma solução revisada, que leva em consideração:

awk '/bar/ {while (/bar/ && getline>0) ; next} 1' foo.txt 

Agora continuamos lendo para pular todas as linhas / bar /.

Hai Vu
fonte
11
Para replicar grep -A100%, você também precisa manipular qualquer número de barlinhas consecutivas corretamente (removendo o bloco inteiro e 1 linha depois).
Jw013
7

Você vai querer usar os recursos de script do sed para fazer isso.

$ sed -e '/bar/ { 
 $!N
 d
 }' sample1.txt

Dados de amostra:

$ cat sample1.txt 
foo
bar
biz
baz
buz

O comando "N" acrescenta a próxima linha de entrada no espaço do padrão. Combinada com a linha da correspondência de padrões (/ bar /), serão as linhas que você deseja excluir. Você pode excluir normalmente com o comando "d".

Michael Rollins
fonte
Como digito uma nova linha no console? Ou isso é apenas script?
jakub.g
@ jakub.g: com GNU sed:sed -e '/bar/{N;d}' sample1.txt
Cyrus
2

Se qualquer linha imediatamente após uma partida for removida, seu sedprograma terá que considerar partidas consecutivas. Em outras palavras, se você remover uma linha após uma correspondência que também corresponda, provavelmente também deverá remover a linha que segue.

É implementado de maneira simples - mas você precisa olhar um pouco para trás.

printf %s\\n     0 match 2 match match \
                 5 6 match match match \
                 10 11 12 match 14 15  |
sed -ne'x;/match/!{g;//!p;}'

0
6
11
12
15

Ele funciona trocando espaços de espera e padrão para cada linha lida - para que a última linha possa ser comparada à corrente a cada vez. Portanto, quando sedlê uma linha, ela troca o conteúdo de seus buffers - e a linha anterior é o conteúdo de seu buffer de edição, enquanto a linha atual é colocada no espaço de espera.

Portanto, sedverifica a linha anterior para uma correspondência e match, se !não for encontrada, as duas expressões na {função }são executadas. sedvai get o espaço hold, substituindo o espaço padrão - o que significa que a linha atual é, em seguida, em ambos os espaços de espera e padrão - e, em seguida, ele irá //verificar se há uma correspondência para a sua expressão regular mais recentemente compilado - match- e se ele não faz matchisso está parrumado.

Isso significa que uma linha só será impressa se não aparecer e a linha imediatamente anterior não . Também renuncia a quaisquer trocas desnecessárias para sequências de es.match matchmatch

Se você quisesse uma versão que pudesse eliminar um número arbitrário de linhas ocorrendo após uma match, seria necessário um pouco mais de trabalho:

printf %s\\n    1 2 3 4 match  \
                match match 8  \
                9 10 11 12 13  \
                14 match match \
                17 18 19 20 21 |
sed -net -e'/match/{h;n;//h;//!H;G;s/\n/&/5;D;}' -ep

... substitua o 5 pelo número de linhas (incluindo a linha correspondente) que você deseja remover ...


1
2
3
4
12
13
14
21
mikeserv
fonte