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.