pesquisando múltiplos arquivos para uma linha com maior número na coluna 3 de linhas correspondentes

1

Eu tenho vários arquivos com conteúdo semelhante a:

arquivo principal1:

test01:6733:4370:5342
test02:7776:2018:1001
test03:9865:5632:1429
test04:8477:4757:1890
test05:8019:8860:5298
test06:5602:3100:6995
test07:1445:2850:2755
test08:10924:2562:4867
test09:2575:1884:1611

arquivo de amostra2:

test01:8777:1060:9236
test02:1322:1211:10837
test04:3737:10175:5219
test05:8467:8988:9739
test06:7452:3100:2709
test08:4707:9047:10578
test09:8669:2867:8233
test10:8615:10002:7056

arquivo de amostra3:

test01:10957:8172:2472
test02:1401:6160:5894
test03:7245:8934:5725
test04:8477:10106:10069
test05:10769:10381:1102
test06:3605:3713:7695
test08:10924:2562:10568
test09:2913:5628:1305
test10:5501:10293:2319

Eu quero atualizar cada linha no arquivo principal1 com uma linha de outro arquivo com a mesma primeira coluna e na terceira coluna com o maior número de todos os arquivos.

Somente as primeiras colunas no arquivo principal devem ser consideradas (teste ##, que existem nos outros arquivos, mas que não existem no arquivo principal, devem ser ignorados).

Quando mais linhas são encontradas nos outros arquivos (com maior, mas o mesmo número na terceira coluna), qualquer uma delas pode ser usada para atualizar o arquivo principal.

aqui está minha solução não ideal

$ awk -F: '{print $1,$3}' main|while read a b;do grep ^${a}: main file*|sort -t":" -rnk4|awk -F: -vb=$b '{if($4>b){print $0;next} else {print ($1=="main")? $0 : NULL}}'|head -1;done
file3:test01:10957:8172:2472
file3:test02:1401:6160:5894
file3:test03:7245:8934:5725
file2:test04:3737:10175:5219
file3:test05:10769:10381:1102
file3:test06:3605:3713:7695
main:test07:1445:2850:2755
file2:test08:4707:9047:10578
file3:test09:2913:5628:1305

como processar todos esses arquivos no awk de uma só vez e fazer o trabalho sem loops while e muitos canais que tenho no meu comando?

Atualização: @RomanPerekhrest, obrigado por seu código impressionante, como adicionar ainda: sufixo atualizado para todas as linhas que vem dos outros arquivos? Eu gostaria de ter algo como:

test01:10957:8172:2472:updated
test02:1401:6160:5894:updated
test03:7245:8934:5725:updated
test04:3737:10175:5219:updated
test05:10769:10381:1102:updated
test06:3605:3713:7695:updated
test07:1445:2850:2755
test08:4707:9047:10578:updated
test09:2913:5628:1305:updated

Atualização: Eu tenho novo caso, que eu não previ antes, que é com os outros arquivos com maior valor em $ 3 mas também não-dígitos na coluna $ 2 - nesse caso essa linha (apesar de $ 3 maior) deve ser ignorada devido a valores errados em US $ 2.

Para mostrar este caso, usando os arquivos de exemplo acima, na linha "test09" do arquivo2, substituo a segunda coluna por "xxxxx" e agora tenho:

$ grep test09 *
file2:test09:xxxxx:2867:8233
file3:test09:2913:5628:1305
main:test09:2575:1884:1611
$ awk -F':' 'FILENAME != "main"{ if ($2~/^[0-9]+/&&(!($1 in a) || ($3 > a[$1]))) { a[$1]=$3; b[$1]=$0 } next }{ if (($1 in a) && (a[$1] > $3)){ print b[$1]":updated"; delete b[$1] } else print  }' file* main
test01:10957:8172:2472:updated
test02:1401:6160:5894:updated
test03:7245:8934:5725:updated
test04:3737:10175:5219:updated
test05:10769:10381:1102:updated
test06:3605:3713:7695:updated
test07:1445:2850:2755
test08:4707:9047:10578:updated
test09:2913:5628:1305:updated <- this is now update from file3

em seguida, alterei o valor de $ 2 na linha "test09" no arquivo3 para não dígitos também:

$ grep test09 *
file2:test09:xxxxx:2867:8233
file3:test09:zzzzz:5628:1305
main:test09:2575:1884:1611
$ awk -F':' 'FILENAME != "main"{ if ($2~/^[0-9]+/&&(!($1 in a) || ($3 > a[$1]))) { a[$1]=$3; b[$1]=$0 } next }{ if (($1 in a) && (a[$1] > $3)){ print b[$1]":updated"; delete b[$1] } else print  }' file* main
test01:10957:8172:2472:updated
test02:1401:6160:5894:updated
test03:7245:8934:5725:updated
test04:3737:10175:5219:updated
test05:10769:10381:1102:updated
test06:3605:3713:7695:updated
test07:1445:2850:2755
test08:4707:9047:10578:updated
test09:2575:1884:1611 <-- this is now from the main file

Embora pareça estar funcionando bem, poderia vir por favor explicar o segundo "se" no código? Também precisa da condição para $2~/^[0-9]+/ ?

{ if (($1 in a) && (a[$1] > $3))
    
por DonJ 24.03.2018 / 20:04

1 resposta

3

Solução awk otimizada que é aproximadamente 27 vezes mais rápida:

awk -F':' 'FILENAME != "main"{ 
               if (!($1 in a) || $3 > a[$1]) { a[$1] = $3; b[$1] = $0 } next; 
           }
           { 
               if (($1 in a) && (a[$1] > $3)){ print b[$1]; delete b[$1] } 
               else print; 
           }' file* main

A saída:

test01:10957:8172:2472
test02:1401:6160:5894
test03:7245:8934:5725
test04:3737:10175:5219
test05:10769:10381:1102
test06:3605:3713:7695
test07:1445:2850:2755
test08:4707:9047:10578
test09:2913:5628:1305

Comparação do tempo de execução:

$ time(awk -F: '{print $1,$3}' main |while read a b; do grep ^${a}: main file* | sort -t":" -rnk4 | awk -F':' -vb=$b '{if($4>b){print $0;next} else {print ($1=="main")? $0 : NULL}}' | head -1; done > /dev/null)

real    0m0.111s
user    0m0.004s
sys 0m0.012s

$ time(awk -F':' 'FILENAME != "main"{ if (!($1 in a) || $3 > a[$1]) { a[$1]=$3; b[$1]=$0 } next }{ if (($1 in a) && (a[$1] > $3)){ print b[$1]; delete b[$1] } else print  }' file* main > /dev/null)

real    0m0.004s
user    0m0.000s
sys 0m0.000s
    
por 24.03.2018 / 21:25

Tags