sed
não é a ferramenta certa para essa tarefa
... mas isso não significa que você não possa abusar dele para fazer seus lances:
sed -n -e "2,$ p" -e "/^~keyword~./ {s/$/§/;p}" in.txt | sed -n '/^~keyword~./,/^~[[:alpha:]]./ {/^~keyword~..*§$/ {s/§$//; b print}; /^~[[:alpha:]]./b; :print p}'
Então, depois de se deitar em uma sala escura para se recuperar dessa abominação, veja o que ela faz:
O que queremos alcançar?
Extraia "blocos" de um arquivo, onde cada "bloco" inicia com uma linha correspondente a regex R1 ("linhas iniciais") e termina com a linha que precede a próxima ocorrência de regex R2 ("linhas terminadoras").
Portanto, use apenas intervalos de padrões de sed
, onde está o problema?
R2 é um subconjunto de R1, então nossas "linhas terminadoras" podem ser o começo de novos blocos. sed
não suporta blocos sobrepostos.
Portanto, crie um regex que corresponda ao R2, mas não corresponda ao R1.
Isso exigiria asserções de comprimento zero, que sed
não tem. (Lembra como eu disse que sed
não era a ferramenta certa para isso?)
Solução: Se procurar pela "linha terminadora" engole as "linhas iniciais", apenas duplique as "linhas iniciais".
Isso funcionará, mas não devemos duplicar a primeira "linha de início", senão vamos ver cada par duplicado como um bloco. 1
sed -n -e "2,$ p" -e "/^~keyword~./ {s/$/§/;p}" in.txt
= Imprime todas as linhas começando na linha número 2 (ou seja, tudo, exceto a linha 1). Também imprima linhas uma segunda vez se elas corresponderem a R1. Eu vou chegar ao s/$/§/
daqui a pouco.
Agora que temos blocos claramente delimitados, use um intervalo de padrões para imprimir todas as linhas entre iniciantes e terminadores de bloco: sed -n '/^~keyword~./,/^~[[:alpha:]]./p'
Oh, espere, isso inclui as linhas terminator. Stack Overflow para o resgate .
Mas não podemos simplesmente pular todas as linhas que combinam com R2 - lembre-se que R1 ⊂ R2, então remover as linhas terminadoras também removeria as linhas iniciais.
"Felizmente", sed
tem ramificação. Que tal imprimirmos tudo que corresponda a R1 e só descartamos as correspondências para R2 depois ?
sed -n '/^~keyword~./,/^~[[:alpha:]]./ {/^~keyword~./b print; /^~[[:alpha:]]./b; :print p}'
Ótimo, agora estamos imprimindo nossas linhas de início duplicadas quando elas são uma linha de terminação ... Se ao menos houvesse uma maneira de distinguir as linhas de início originais e suas duplicatas…
É por isso que temos que s/$/§/
: adiciona §
no final de cada linha de início duplicada (note que as linhas iniciais duplicadas do § terminarão sendo as que iniciam um bloco, e as unidas As linhas iniciais serão os blocos termais que são imediatamente seguidos por outro bloco.
Agora, temos todas as informações necessárias para fazer uma verificação e ramificação mais detalhada:
Para todas as linhas dentro de um intervalo de blocos…
- Verifique se a linha corresponde a R1 e tem um § à direita.
Em caso afirmativo, remova o § e salte para imprimir a linha.
- Caso contrário (ou seja, se não pularmos), remova todas as linhas que correspondam a R2 ignorando todos os outros comandos (incluindo a impressão).
- Finalmente, imprima a linha atual.
{/^~keyword~..*§$/ {s/§$//; b print}; /^~[[:alpha:]]./b; :print p}
Resultado final:
sed -n -e "2,$ p" -e "/^~keyword~./ {s/$/§/;p}" in.txt | sed -n '/^~keyword~./,/^~[[:alpha:]]./ {/^~keyword~..*§$/ {s/§$//; b print}; /^~[[:alpha:]]./b; :print p}'
No entanto, isso pressupõe que a primeira linha de início do arquivo (correspondente a R1) esteja na linha 1 (lembre-se de que essa é a única linha que excluímos ao duplicar as linhas iniciais). Se não for, você receberá pares limpos, mas nenhum dado:
~keyword~, ~output~.
~keyword~, ~output~.
Você provavelmente poderia adicionar mais correspondência e ramificação para contornar isso, mas realmente…
use apenas awk
.