don's podem ser melhores na maioria dos casos, mas apenas no caso de o arquivo ser realmente grande, e você não pode obter sed para manipular um arquivo de script tão grande acontecer em torno de 5000 + linhas de script) , aqui está com sed :
sed -ne:t -e"/\n.*$match/D" \
-e'$!N;//D;/'"$match/{" \
-e"s/\n/&/$A;t" \
-e'$q;bt' -e\} \
-e's/\n/&/'"$B;tP" \
-e'$!bt' -e:P -e'P;D'
Este é um exemplo do que é chamado de janela deslizante na entrada. Ele funciona criando um buffer de look-ahead de $B -count linhas antes de tentar imprimir qualquer coisa.
E, na verdade, provavelmente devo esclarecer meu ponto anterior: o limitador principal de desempenho para essa solução e para o don estará diretamente relacionado ao intervalo. Esta solução diminuirá com intervalos maiores , enquanto os don's diminuirão com freqüências de intervalos maiores . Em outras palavras, mesmo que o arquivo de entrada seja muito grande, se a ocorrência real do intervalo ainda for muito pouco frequente, sua solução provavelmente será o caminho a ser seguido. No entanto, se o tamanho do intervalo for relativamente gerenciável e for provável que ocorra com frequência, essa é a solução que você deve escolher.
Então aqui está o fluxo de trabalho:
- Se
$matchfor encontrado no espaço de padrão precedido por\newline,sedrecursivamenteDexcluirá cada\newline que o precede.- Eu estava limpando completamente o espaço de padrão de
$match- mas, para lidar facilmente com a sobreposição, deixar um ponto de referência parece funcionar muito melhor. - Eu também tentei
s/.*\n.*\($match\)//tentar obtê-lo de uma só vez e evitar o loop, mas quando$A/$Bsão grandes, o loopDelete é consideravelmente mais rápido.
- Eu estava limpando completamente o espaço de padrão de
- Em seguida, inserimos a linha
Next da entrada precedida por um delimitador\newline e tentamos novamenteDelete a/\n.*$match/referindo-se à nossa expressão regular mais recentemente usada w ///. - Se o espaço do padrão corresponder a
$match, ele só poderá fazê-lo com$matchna cabeça da linha - todas as$Bantes de as linhas terem sido limpas.- Então, começamos a fazer o loop em
$After. - Cada execução desse loop tentará
s///ubstitute para&o caractere$Ath\newline no espaço padrão e, se tiver êxito,test nos ramificará - e todo o nosso$After buffer - fora do script inteiramente para iniciar o script do topo com a próxima linha de entrada, se houver. - Se o
test não for bem-sucedido,branch voltará para o rótulo:top e recursará para outra linha de entrada - possivelmente iniciando o loop se$matchocorrer ao coletar$Aapós.
- Então, começamos a fazer o loop em
- Se passarmos de um loop de função
$match, tentaremosprint a última linha$, e se!não tentars///ubstitute por&em si o caractere de ewline$Bth\nno espaço padrão.- Também
test isto e, se for bem-sucedido, vamos ramificar para o rótulo:Print. - Se não, voltaremos para
:top e obteremos outra linha de entrada anexada ao buffer.
- Também
- Se chegarmos a
:Print,Print, em seguida,Dserá excluído até o primeiro\newline no espaço padrão e executaremos novamente o script a partir do topo com o que resta.
E desta vez, se estivéssemos a fazer A=2 B=2 match=5; seq 5 | sed...
O espaço padrão para a primeira iteração em :P rint seria semelhante:
^1\n2\n3$
E é assim que sed reúne seu buffer $B fore. E assim, sed imprime para a saída $B -count linhas por trás da entrada que reuniu. Isso significa que, dado o nosso exemplo anterior, sed iria P rint <%>1 para a saída, e então D elete isso e mandaria de volta para o topo do roteiro um espaço padrão que parece:
^2\n3$
... e na parte superior do script, a linha de entrada N ext é recuperada e, assim, a próxima iteração se parece com:
^2\n3\n4$
E quando encontramos a primeira ocorrência de 5 na entrada, o espaço padrão parece:
^3\n4\n5$
Em seguida, o loop D elete entra em ação e, quando passa, parece que:
^5$
E quando a linha de entrada N ext é extraída, sed atinge o EOF e sai. Por esse tempo, ele tem apenas P rinted linhas 1 e 2.
Veja um exemplo de execução:
A=8 B=7 match='[24689]0'
seq 100 |
sed -ne:t -e"/\n.*$match/D" \
-e'$!N;//D;/'"$match/{" \
-e"s/\n/&/$A;t" \
-e'$q;bt' -e\} \
-e's/\n/&/'"$B;tP" \
-e'$!bt' -e:P -e'P;D'
Isso imprime:
1
2
3
4
5
6
7
8
9
10
11
12
29
30
31
32
49
50
51
52
69
70
71
72
99
100