Obtém o texto entre o padrão inicial e o padrão final com base no padrão entre o padrão inicial e final [duplicado]

2

Estou tentando obter tudo entre startStr e endStr para o caso bbb . Entendo como posso obter todas as ocorrências entre startStr e endStr usando sed . Eu não vejo como eu iria limitá-lo apenas para a instância em que bbb ocorre.

Exemplo de entrada:

fff
startStr
aaa
bbb
ccc
endStr
xxx
yyy
startStr
ddd
endStr
ddd
bbb

Resultado exigido:

startStr
aaa
bbb
ccc
endStr

Isso é o que eu tenho:

$ sed -n -e '/startStr/,/endStr/ p' sample.txt
startStr
aaa
bbb
ccc
endStr
startStr
ddd
endStr
    
por Tomas Greif 06.01.2017 / 23:37

5 respostas

4

Para o primeiro startStrendStr , contém /bbb/ ocorrência:

 sed -n '/startStr/ {:n; N; /endStr/ {/\n[^\n]*bbb[^\n]*\n/ {p; q}; b}; bn}'

ou

sed -n '/startStr/ {:n; N; /endStr/ {/\nbbb\n/ {p; q}; b}; bn}'

if bbb não é uma expressão regular e é exatamente uma string que você precisa (do começo ao \n ).

Explicação

Para o endereço /startStr/ we:

  • definir rótulo :n ;
  • leia a próxima linha com N ;
  • verifique se corresponde a /endStr/ ;
    • se for verdade, verifique /\nbbb\n/ ocorrência neste bloco que lemos;
      • se estiver presente, faça {p; q} para «imprimir e sair»,
      • caso contrário, faça b para «lançar este bloco e começar a procurar na próxima»;
  • se não for o fim do bloco, passamos para :n , ou seja, continuamos lendo.
por 07.01.2017 / 00:14
2

Eu recomendo pcregrep para este trabalho:

pcregrep -M 'startStr(.|\n)*?bbb(.|\n)*?endStr' sample.txt

A opção -M permite corresponder padrões de várias linhas e o operador *? in greedy. O resto deveria ser óbvio.

    
por 06.01.2017 / 23:58
2

Amostra de entrada modificada para incluir o bloco de startStr...endStr sem bbb antes de corresponder ao bloco

$ cat ip.txt 
startStr
foo
bar
endStr
fff
baz
startStr
aaa
bbb
ccc
endStr
xxx
yyy
startStr
ddd
endStr
ddd
bbb


awk solution

awk '/startStr/{f=1; m=0; buf = $0; next}
     /bbb/ && f{m=1}
     f{buf = buf ORS $0}
     /endStr/ && f{f=0; if(m==1)print buf}
    ' ip.txt
  • /startStr/{f=1; m=0; buf = $0; next} set flag para indicar o início do bloco, limpar correspondência, inicializar buffer e passar para a próxima linha
  • /bbb/ && f{m=1} se a linha contiver bbb , definir correspondência. f é usado para evitar a correspondência de bbb fora de startStr...endStr
  • f{buf = buf ORS $0} desde que o sinalizador esteja definido, acumule linhas de entrada
  • /endStr/ && f{f=0; if(m==1)print buf} no final do bloco, buffer de impressão se a correspondência foi encontrada


como uma linha:

$ awk '/startStr/{f=1; m=0; buf = $0; next} /bbb/ && f{m=1} f{buf = buf ORS $0} /endStr/ && f{f=0; if(m==1)print buf}' ip.txt 
startStr
aaa
bbb
ccc
endStr


Uma solução perl mais simples, fazendo slurping todo o arquivo de entrada - presume que não há blocos como startStr...startStr...endStr (ou seja, nenhum endStr para a primeira startStr)

$ perl -0777 -ne '(@m) = /startStr.*?endStr\n/gs; print grep { /bbb/ } @m' ip.txt 
startStr
aaa
bbb
ccc
endStr
    
por 07.01.2017 / 05:17
1

Solução Python:

$ ./find_bound_pattern.py < input.txt                                                                                    
startStr
aaa
bbb
ccc
endStr

Script em si:

#!/usr/bin/env python
from __future__ import print_function
import sys

flag = None
group = []
for line in sys.stdin:
    if 'startStr' == line.strip():
        flag = True # mark beginning of the block
        group.append(line.strip())
        continue
    if flag: # we are in a block, so record lines
        group.append(line.strip())
    if  'endStr' == line.strip():
        flag = False # reached end of block, time to check
        if 'bbb' in group:
            print('\n'.join(group))
        group = [] # clear list after each block end

A maneira como isso funciona é bem simples: marcamos o início do bloco com a variável flag , e desmarcamos quando chegamos ao fim do bloco. Quando chegarmos ao fim do bloco, verificamos o que gravamos para a presença de bbb e imprimimos todas as strings. A lista gravada é apagada no final de cada bloco e o processo é repetido novamente, portanto, isso é adequado para a correspondência de vários blocos que podem conter bbb .

A lógica desta abordagem é simples, pode ser implementada com outras linguagens, como C, Java ou Ruby - o que o seu coração desejar. Observe que, nesse caso, isso é mais um problema de correspondência de linha, mas se houver necessidade de correspondência mais avançada de padrões, ela também poderá ser implementada via re module.

    
por 07.01.2017 / 09:38
-1
sed -n -e '/startStr/,/bbb/p;/bbb/,/endStr/p' /path/to/input
    
por 06.01.2017 / 23:47