Como extrair cadeia entre dois padrões usando sed no Linux, enquanto segundo padrão repetindo mais linhas no arquivo

1

Por exemplo,

xyz
A1
B1
C1
D1
End
End
End
X1
X2
X3
Done

Eu quero extrair todas as strings entre xyz e End pattern. Então a saída deve ser

xyz
A1
B1
C1
D1
End
End
End
    
por Pankaj 10.07.2017 / 14:55

5 respostas

1

Método-a

perl -l -0777ne 'print /^(xyz.*?^End$(?:\nEnd$)*)/ms' yourfile

Trabalhando

  • Slurp o arquivo para que ele apareça como uma string looooong que pode ser desmembrada usando o regex apropriado. O regex neste caso é:
    • procure por xyz no início de uma linha (não necessariamente no arquivo).
    • procure o End on a line mais próximo e depois quantas linhas consecutivas.

Método 2

perl -lne '
   next unless /xyz/ ... eof;
   last if !/End/ and $flag;
   $flag ||= 1 if /End/;
   print;
' yourfile

Trabalhando

  • Aqui operamos o Perl em uma base por linha e configuramos uma pequena máquina de estado.
    • Rejeitar qualquer porção fora do intervalo do arquivo.
    • Quando entramos no intervalo certo, imprimimos todas as linhas até atingirmos a linha / End /. Nesse momento, colocamos a bandeira.
    • Então, saímos assim que vemos a primeira linha não final / final.

Método 3

sed -e '
   /xyz/!d
   :a
      $q;N
   /\nEnd$/!ba
   :b
      n
   /End/bb
   d
' yourfile

Neste método, operamos o primeiro loop do-while (: a) que irá acumular linhas começando de / xyz / para / End /.

O segundo loop do-while (: b) imprime linhas até a próxima linha ser / End /.

Método 4

sed -e '
   /xyz/,/End/!d
   H;/xyz/h;/End/!d
   :a
      $q;N
      /\(.*\)\n$/!{g;q;}
      s/.*\n//;H
   ba
' yourfile

Com esse método, primeiro selecionamos o intervalo correto e, em seguida, armazenamos esses dados de intervalo no espaço de armazenamento. O loop do-while (: a) é o setup que acrescenta incrementalmente ao espaço hold enquanto a próxima linha passa a ser / End /.

Resultados

xyz
A1
B1
C1
D1
End
End
End
    
por 11.07.2017 / 18:41
0

Este é um tipo de trabalho pcregrep é bom em:

pcregrep -M 'xyz(.|\n)*End' file

Note que é muito ganancioso e come tudo até o final End , incluindo outros End s.

    
por 10.07.2017 / 15:12
0

Perl para o resgate: imprima todas as linhas entre o primeiro xyz e o último End :

 perl -ne '
     $inside = 1        if /^xyz$/;
     $seen_end = 1      if $inside && /^End$/;
     push @buff, $_     if $inside;
     print splice @buff if /^End$/ && @buff;
' input-file

A partir da primeira ocorrência de xyz , começamos a empurrar todas as linhas para um buffer. Uma vez que End é encontrado, nós saímos e limpamos o buffer (veja emenda ), mas continuamos a empurrar linhas para o buffer no caso de haver foi outro End depois.

    
por 10.07.2017 / 15:08
0

Como você está pedindo uma solução sed , eu faria assim:

sed -e '/^xyz$/!d;:a' -e '$!{N;ba' -e '};s/\(.*\nEnd\).*//'

Portanto, descarte tudo antes do primeiro padrão ( /^xyz$/!d ), depois faça um loop para coletar todas as linhas restantes no espaço padrão ( :a;$!{N;ba ) e remova tudo por trás da última ocorrência do segundo padrão ( s/\(.*\nEnd\).*// ). / p>

A coleta no espaço padrão é necessária, pois o endereçamento ( /xyz/,/End/ ) não é ganancioso, mas .* dentro do espaço padrão é.

    
por 10.07.2017 / 15:37
0
Solução

awk :

awk '/xyz/,/End/{ print $0; n=NR }($0=="End" && n && NR>n && NR-n++ == 1)' file

A saída:

xyz
A1
B1
C1
D1
End
End
End
  • /xyz/,/End/ - intervalo de registros, de xyz a End

  • n=NR - capturando o número do registro (na correspondência de intervalo - eventualmente conterá o número do último registro do intervalo)

por 10.07.2017 / 15:37