Dada a entrada que você mostra, o seguinte deve funcionar:
<infile sed -e '$!N;2i\' -e '3k
s|\(\(.* *\).*_1\)\n\(\(.* *\).*_2\)$|[ ]Pd+/p[]pc|;t
s|^[ _ID0-9]*|[&]pc|;P;D' | dc
Para mim, imprime ...
10 ID1_1 .333
20 ID1_2
1 ID3_1 .100
9 ID3_2
20 ID20_1
15 ID2_1 .047
300 ID2_2
... porque eu configurei a precisão de dc para 3, mas com uma precisão de 10 ...
10 ID1_1 .3333333333
20 ID1_2
1 ID3_1 .1000000000
9 ID3_2
20 ID20_1
15 ID2_1 .0476190476
300 ID2_2
Além da precisão de saída, ela também difere do seu resultado esperado na terceira linha - mas eu acho que isso é mais devido a um erro de digitação na pergunta?
De qualquer forma, para entender isso, você deve considerar que primeiro devo analisar a saída em dois formulários para dc - dc P rint [string] sem a seguinte \n ewline, ou será p rint um número ou [string] com um. Além disso, possivelmente e x ecuting uma string como dc macro, pode fazer pouco mais com eles. Mas, com números, é muito capaz.
Portanto, com sed , primeiro anexei a linha $!N ext à atual, se a linha atual for ! the $ last. Na segunda linha, eu i nster a string 3k para stdout - que é um comando dc para definir a precisão como 3.
Então eu tentei uma substituição:
s|\(\(.* *\).*_1\)\n\(\(.* *\).*_2\)$|[ ]Pd+/p[]pc|
Isso só será bem-sucedido se o espaço padrão contiver pelo menos um espaço seguido em algum momento por _1 seguido imediatamente por um caractere \n ewline seguido em algum ponto por pelo menos um espaço seguido em algum ponto por _2 seguido imediatamente pelo $ final do espaço padrão.
Isso significa que a substituição acima afeta apenas pares de linhas como ...
...ID_1
...ID_2
... e não outros. Quando isso afeta, ele transforma seu conteúdo em um script dc viável. Ele é o próximo t ests se a substituição foi bem-sucedida e, em caso afirmativo, ela se ramifica do script, imprimindo assim os resultados da substituição e não executando mais comandos sed . dc usa a saída padrão de sed como entrada padrão, assim, por exemplo, depois que sed altera as duas primeiras linhas para ficar assim:
[ 10 ID_1 ]P10d20+/p[ 20 ID_2]pc
... dc lida com essa entrada da seguinte maneira:
-
[ 10 ID_1 ] - empurre a string entre os colchetes para o topo da pilha (o que empurra tudo já na pilha para baixo em um)
-
P - P rint o topo da pilha sem um \n ewline e pop off (o que coloca todos os valores na pilha abaixo em um)
-
10 - empurre o número 10 para o topo da pilha
-
d - d uplique o topo da pilha
-
20 - insira o número 20 no topo da pilha
-
+ - adicione o topo da pilha e o 2cd do topo da pilha (enquanto estala ambos) e empurre o resultado para o topo da pilha
-
/ - divide o 2cd do topo da pilha (agora nosso d uplicated 10 ) pelo topo da pilha (nosso resultado 10 20 + ) (enquanto estala ambos) e empurre o resultado para o topo da pilha
-
p - p rint o topo da pilha (sem aparecê-lo) seguido por um \n ewline à direita.
-
[ 20 ID_2] - empurra a corda para o topo da pilha
-
p - p rint o topo da pilha (novamente, sem aparecê-lo) seguido por um \n ewline à direita
-
c - c aprende a pilha
E assim dc imprime:
10 ID1_1 .333
20 ID1_2
Mas se sed não coincidir e alterar o espaço de padrão com êxito conforme já descrito, ele ficará com outras linhas para manipular. Nesse caso, sed imprime a primeira seqüência de [ ID_0-9]* entre [ e ] , além de anexar os comandos pc . Em seguida, ele usa P rints de espaço de padrão até o primeiro \n ewline que está ocorrendo no espaço padrão e, em seguida, D eletria o mesmo antes de recomeçar com o que resta. E assim sed trabalha em uma linha de frente, imprimindo o script dc em dc a todo momento.
Isso significa que o arquivo inteiro é processado in-stream, pois dc e sed fornecem saída enquanto processam. Dessa forma - desde que sua entrada seja semelhante ao exemplo da pergunta - você poderia facilmente processar 2 milhões de linhas da mesma maneira, ou então manipular um arquivo de log em tempo real.