sed ou awk: exclui n linhas seguindo um padrão

105

Como eu misturaria padrões e intervalos numéricos no sed (ou qualquer ferramenta semelhante - awk por exemplo)? O que eu quero fazer é combinar certas linhas em um arquivo e excluir as próximas n linhas antes de continuar, e eu quero fazer isso como parte de um pipeline.

Martin DeMello
fonte

Respostas:

187

Eu vou tentar isso.

Para excluir 5 linhas após um padrão (incluindo a linha com o padrão):

sed -e '/pattern/,+5d' file.txt

Para excluir 5 linhas após um padrão (excluindo a linha com o padrão):

sed -e '/pattern/{n;N;N;N;N;d}' file.txt
dogbane
fonte
14
Observe que o +Npadrão é uma extensão GNU. Mude o primeiro npara um Nem seu segundo exemplo para fazê-lo incluir a linha com o padrão.
Pausado até novo aviso.
2
como excluir todas as linhas depois que o padrão é correspondido? Estou usando sed -e '/ <! - # content end -> </div> /, $ d' out.txt, mas dá um erro dizendo: sed: -e expressão # 1, char 24: caracteres extras após comando Agradecemos antecipadamente.
N mol
8
O que está acontecendo é semelhante, mas ligeiramente diferente em cada caso. Na primeira receita, /pattern/,+5define um intervalo, que começa com uma linha contendo "padrão" ( /pattern/) e termina 5 linhas depois ( +5). O último caractere dé um comando a ser executado em cada linha desse intervalo, que é "excluir". Na segunda receita, em vez de combinar um intervalo, ele corresponde apenas à linha que contém o padrão ( /pattern/) e, em seguida, executa uma série de comandos {n;N;N;N;N;d}:, que basicamente imprime a próxima linha ( n) e então lê e finalmente descarta as próximas 4 linhas ( N;N;N;N;d)
pimlottc
18
Em sistemas Mac / OS X, você precisa adicionar um ponto-e-vírgula antes do colchete de fechamento:sed -e '/pattern/{n;N;N;N;N;d;}' file.txt
AvL
1
Para completar: Para excluir todas as linhas que seguem um certo padrão, something faça :, sed -E '/^something$/,$d'onde -Eé o regex estendido de portabilidade POSIX.
not2qubit
7

Sem extensões GNU (por exemplo, no macOS):

Para excluir 5 linhas após um padrão (incluindo a linha com o padrão)

 sed -e '/pattern/{N;N;N;N;d;}'

Adicione -i ''para editar no local.

thakis
fonte
6

awkSoluções simples :

Suponha que a expressão regular a ser usada para encontrar linhas correspondentes seja armazenada na variável shell $regexe a contagem de linhas a serem puladas $count.

Se a linha correspondente também deve ser ignorada (as $count + 1linhas são ignoradas):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; next } --skip >= 0 { next } 1'

Se a linha correspondente deveria não ser pulada ($countlinhas após a correspondência são puladas):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; print; next } --skip >= 0 { next } 1'

Explicação:

  • -v regex="$regex" -v count="$count"define awkvariáveis ​​com base em variáveis ​​de shell de mesmo nome.
  • $0 ~ regex corresponde à linha de interesse
    • { skip=count; next }inicializa a contagem de pulados e prossegue para a próxima linha, pulando efetivamente a linha correspondente; na 2ª solução, oprint antes nextgarante que é não ignorado.
    • --skip >= 0 diminui a contagem de saltos e executa uma ação se (ainda)> = 0, o que implica que a linha em questão deve ser saltada.
    • { next } prossegue para a próxima linha, efetivamente pulando a linha atual
  • 1é uma abreviação comumente usada para { print }; ou seja, a linha atual é simplesmente impressa
    • Somente linhas não correspondentes e não puladas alcançam este comando.
    • A razão de que 1é equivalente a { print }é que 1é interpretado como um padrão booleano que por definição sempre avalia como verdadeiro, o que significa que sua ação associada (bloco) é executada incondicionalmente. Como não há nenhuma ação associada neste caso, o awkpadrão é imprimir a linha.
mklement0
fonte
3

Isso pode funcionar para você:

cat <<! >pattern_number.txt
> 5 3
> 10 1
> 15 5
> !
sed 's|\(\S*\) \(\S*\)|/\1/,+\2{//!d}|' pattern_number.txt |
sed -f - <(seq 21)
1 
2
3
4
5
9
10
12
13
14
15
21
potong
fonte
10
Uau, isso é enigmático.
pimlottc
3
Uma solução inteligente (embora específica do GNU-Sed), mas poucas pessoas se beneficiarão dela, a menos que você adicione uma explicação. pattern_number.txté um arquivo de 2 colunas contendo o padrão a ser correspondido na 1ª coluna e na 2ª o número de linhas a pular. O primeiro sedcomando transforma o arquivo em um sedscript que executa a correspondência e o salto correspondentes; esse script é fornecido via -fe stdin ( -) para o segundo sedcomando. O segundo sedcomando opera em um arquivo de entrada ad-hoc de amostra formado a partir da saída de seq 21para demonstrar que funciona.
mklement0 de
Além disso, a solução vem com uma ressalva: o método que ele usa para não pular a primeira linha (aquela que corresponde ao padrão) tem o efeito colateral de também não pular linhas duplicadas no intervalo.
mklement0
Esse é um uso impressionante do sed.
Travis Rodman
3

Usando Perl

$ cat delete_5lines.txt
1
2
3
4
5 hello
6
7
8
9
10
11 hai
$ perl -ne ' BEGIN{$y=1} $y=$.  if /hello/ ; print if $y==1 or $.-$y > 5 ' delete_5lines.txt
1
2
3
4
11 hai
$
stack0114106
fonte
2

Esta solução permite que você passe "n" como parâmetro e ele lerá seus padrões de um arquivo:

awk -v n=5 '
    NR == FNR {pattern[$0]; next}
    {
        for (patt in pattern) {
            if ($0 ~ patt) {
                print # remove if you want to exclude a matched line
                for (i=0; i<n; i++) getline
                next
            }
        }
        print
    }
' file.with.patterns -

O arquivo chamado "-" significa stdin para awk, então ele é adequado para o seu pipeline

glenn jackman
fonte
2
awk é capaz de ser muito mais perl do que eu imaginava!
Martin DeMello