don's pode ser melhor na maioria dos casos, mas caso o arquivo seja realmente grande e você não consiga sed
lidar com um arquivo de script tão grande (o que pode acontecer em mais de 5000 linhas de script) , aqui está o seguinte 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 antecipado de $B
linhas -count antes de tentar imprimir qualquer coisa.
E, na verdade, provavelmente devo esclarecer meu argumento anterior: o limitador de desempenho primário para essa solução e don's estará diretamente relacionado ao intervalo. Esta solução irá diminuir com intervalo maior tamanhos , enquanto Don irá diminuir com intervalo maior frequência . 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 é o caminho a percorrer. No entanto, se o tamanho do intervalo for relativamente gerenciável e provavelmente ocorrer com freqüência, então essa é a solução que você deve escolher.
Então, aqui está o fluxo de trabalho:
- Se
$match
for encontrado no espaço do padrão precedido por uma linha de \n
ew, elimina sed
recursivamente D
cada linha de \n
ew que o precede.
- Eu estava limpando
$match
o espaço do padrão completamente antes - mas lidar facilmente com a sobreposição, deixar um ponto de referência parece funcionar muito melhor.
- Também tentei
s/.*\n.*\($match\)/\1/
fazê-lo de uma só vez e desviar do loop, mas quando $A/$B
são grandes, o D
loop elete se mostra consideravelmente mais rápido.
- Em seguida, puxamos a
N
linha ext de entrada precedida por um \n
delimitador de linha ew e tentamos novamente D
excluir uma /\n.*$match/
vez mais, referindo-se à nossa expressão regular usada mais recentemente com //
.
- Se o espaço do padrão corresponder
$match
, ele poderá fazê-lo apenas $match
no início da linha - todas as $B
linhas anteriores foram limpas.
- Então começamos a repetir depois
$A
.
- Cada execução deste ciclo vamos tentar
s///
ubstitute para &
si o $A
th \n
personagem ewline no espaço de padrões, e, se bem sucedida, t
est nos ramificar - e toda a nossa $A
tampão epois - fora do roteiro inteiramente para iniciar o script ao longo do topo com a próxima linha de entrada, se houver.
- Se o
t
est não for bem-sucedido, b
voltaremos ao :t
rótulo op e recuaremos para outra linha de entrada - possivelmente iniciando o loop se $match
ocorrer durante a coleta $A
posterior.
- Se passar por um
$match
circuito de função, então vamos tentar p
rint a $
última linha, se é isso, e se !
não tentar s///
ubstitute para &
si o $B
th \n
personagem ewline no espaço padrão.
- Também determinaremos
t
isso e, se for bem-sucedido, ramificaremos para o :P
rótulo da rint.
- Caso contrário, voltaremos à
:t
operação e obteremos outra linha de entrada anexada ao buffer.
- Se fizermos o rint , o rint
:P
será eliminado até o primeiro ewline no espaço do padrão e reexecutar o script de cima com o que resta.P
D
\n
E desta vez, se estivéssemos fazendo A=2 B=2 match=5; seq 5 | sed...
O espaço do padrão para a primeira iteração no :P
rint seria semelhante a:
^1\n2\n3$
E é assim que sed
reúne seu $B
buffer anterior. E assim sed
imprime $B
nas linhas de contagem de saída atrás da entrada que ela coletou. Isto significa que, dado nosso exemplo anterior, sed
seria P
rint 1
para a saída, e depois D
elete isso e enviar de volta para o topo do script um espaço padrão que se parece com:
^2\n3$
... e na parte superior do script, a N
linha de entrada ext é recuperada e, portanto, a próxima iteração se parece com:
^2\n3\n4$
E assim, quando encontramos a primeira ocorrência de 5
in input, o espaço do padrão se parece com:
^3\n4\n5$
Em seguida, o D
loop elete entra em ação e, quando termina, parece:
^5$
E quando a N
linha de entrada ext é puxada, sed
atinge EOF e sai. Naquela época, apenas P
as linhas 1 e 2 foram criadas.
Aqui está 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
$A
e / ou$B
. Quanto maiores esses números, mais lento ele fica - mas você pode aumentá-los razoavelmente.Você pode usar
gnu grep
com-A
e-B
imprimir exatamente as partes do arquivo que deseja excluir, mas adicionar a-n
opção para também imprimir os números de linha, formatar a saída e passá-la como um script de comandosed
para excluir essas linhas:Isso também deve funcionar com arquivos de padrões passados para
grep
via,-f
por exemplo:Eu acho que isso poderia ser um pouco otimizado se ele colapsasse três ou mais números de linhas consecutivos em intervalos, de modo a ter, por exemplo, em
2,6d
vez de2d;3d;4d;5d;6d
... embora se a entrada tiver apenas algumas correspondências, não valha a pena.Outras maneiras que não preservam a ordem das linhas e são provavelmente mais lentas:
com
comm
:comm
requer entrada classificada, o que significa que a ordem das linhas não seria preservada na saída final (a menos que seu arquivo já esteja classificado); portanto,nl
é usada para numerar as linhas antes da classificação,comm -13
imprime apenas linhas exclusivas para o 2º ARQUIVO ecut
remove a parte adicionada pornl
(ou seja, o primeiro campo e o delimitador:
)com
join
:fonte
comm
fosse mais rápida que a original comsed
egrep
?Se você não se importa em usar
vim
:-Nes
ativa o modo ex silencioso e não compatível. Útil para scripts.+{command}
diga ao vim para executar{command}
no arquivo.g/${PAT}/
- em todas as linhas correspondentes/fff/
. Isso fica complicado se o padrão contiver caracteres especiais de expressão regular que você não pretendia tratar dessa maneira..-${B}
- a partir de 1 linha acima desta.+${A}
- a 2 linhas abaixo desta (veja:he cmdline-ranges
para estas duas)d
- exclua as linhas.+w !tee
depois escreve na saída padrão.+q!
fecha sem salvar as alterações.Você pode pular as variáveis e usar o padrão e os números diretamente. Eu os usei apenas para fins de clareza.
fonte
Que tal (usando GNU
grep
ebash
):Aqui, encontramos as linhas a serem descartadas e
grep -B2 -A1 'fff' file.txt
, em seguida, usamos isso como um arquivo de entrada para encontrar as linhas desejadas descartando-as.fonte
kos
a solução (agora excluída), como se houvesse linhas duplicadas no arquivo de entrada e algumas delas estivessem fora do intervalo e outras dentro desse intervalo, isso excluirá todas elas. Além disso, com várias ocorrências de padrão , se houver linhas como--
no arquivo de entrada (fora dos intervalos), elas serão excluídas porque o delimitador--
aparecerá nagrep
saída da saída quando mais de uma linha corresponder ao padrão (a última é altamente improvável, mas vale a pena). mencionando eu acho).Você pode obter um resultado suficientemente bom usando arquivos temporários:
O resultado é bom o suficiente, pois você pode perder alguma indentação no processo, mas se for um arquivo insensível a xml ou indentação, não deverá ser um problema. Como esse script usa uma unidade ram, escrever e ler esses arquivos temporários é tão rápido quanto trabalhar na memória.
fonte
Além disso, se você quiser excluir algumas linhas antes de um determinado marcador, poderá usar:
(glenn jackman em /programming//a/1492538 )
Ao canalizar alguns comandos, você pode obter o comportamento antes / depois:
fonte
awk
em um arquivo invertido para lidar com as seguintes linhas quando você quiser afetar as linhas anteriores e reverter o resultado.Uma maneira de conseguir isso, talvez a maneira mais fácil seja criar uma variável e fazer o seguinte:
Dessa forma, você ainda tem sua estrutura. E você pode ver facilmente do liner o que você está tentando remover.
fonte
Se houver apenas 1 correspondência:
Caso contrário (awk):
fonte