os don's podem ser melhores na maioria dos casos, mas apenas no caso do 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 intervalos maiores freqüências . Em outras palavras, mesmo que o arquivo de entrada seja muito grande, se a ocorrência real do intervalo ainda for muito rara, sua solução provavelmente é 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
$match
for encontrado no espaço de padrão precedido por\n
ewline,sed
recursivamenteD
excluirá cada\n
ewline 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/$B
é grande, o loopD
elete é consideravelmente mais rápido.
- Eu estava limpando completamente o espaço de padrão de
- Em seguida, inserimos a linha
N
ext da entrada precedida por um delimitador\n
ewline e tentamos novamenteD
elete a/\n.*$match/
referindo-se à expressão regular mais recentemente usada w ///
. - Se o espaço de padrão corresponder a
$match
, só poderá fazê-lo com$match
na parte superior da linha - todas as$B
antes de as linhas terem sido limpas.- Então, começamos a fazer o loop em
$A
fter. - Cada execução deste loop tentará
s///
ubstitute para&
o caractere$A
th\n
ewline no espaço padrão e, se tiver êxito,t
est nos ramificará - e todo o nosso$A
fter buffer - fora do script inteiramente para iniciar o script do topo com a próxima linha de entrada, se houver. - Se o
t
est não for bem-sucedido,b
ranch voltará ao rótulo:t
op e recorrerá a outra linha de entrada - possivelmente iniciando o loop se$match
ocorrer ao coletar$A
após.
- Então, começamos a fazer o loop em
- Se passarmos de um loop de função
$match
, tentaremosp
rint a última linha$
, e se!
não tentars///
ubstitute por&
em si o caractere de ewline$B
th\n
no espaço padrão.- Estaremos
t
est isto também e, se for bem-sucedido, vamos ramificar para o rótulo:P
rint. - Se não, voltaremos para
:t
op e obteremos outra linha de entrada anexada ao buffer.
- Estaremos
- Se chegarmos a
:P
rint,P
rint, em seguida,D
será excluído até o primeiro\n
ewline no espaço padrão e executaremos novamente o script a partir do topo com o que resta.
E então, desta vez, se estivéssemos fazendo A=2 B=2 match=5; seq 5 | sed...
O espaço padrão para a primeira iteração em :P
rint seria semelhante a:
^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 assim, quando encontramos a primeira ocorrência de 5
na entrada, o espaço padrão realmente se parece com:
^3\n4\n5$
Em seguida, o loop D
elete entra em ação e, quando passa por ele, parece:
^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