複数のファイルから行に一致する3列の数字が高い行を検索します。

複数のファイルから行に一致する3列の数字が高い行を検索します。

次のようなコンテンツを含む複数のファイルがあります。

基本ファイル1:

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

サンプルファイル2:

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

サンプルファイル3:

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

メインファイル1のすべての行を、最初の列が同じで、3番目の列のすべてのファイルのうち、最も多数の異なるファイルの行に更新したいと思います。

基本ファイルの最初の列のみを考慮する必要があります(他のファイルにはあるが基本ファイルにはないtest##は無視する必要があります)。

他のファイルでより多くの行が見つかった場合(3番目の列の数は大きいが量は同じです)、そのうちの1つを使用してデフォルトのファイルを更新できます。

これは最適な解決策ではありません。

$ 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

awkでこれらすべてのファイルを一度に処理し、コマンドにwhileループと多くのパイプなしで操作を完了するにはどうすればよいですか?

更新:@RomanPerekhrest、素晴らしいコードを提供していただきありがとうございます。他のファイルのすべての行に:updatedサフィックスを追加するにはどうすればよいですか?私は次のようなものが欲しい:

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

更新:以前に予測できなかった新しいケースがあります。他のファイルの$ 3の値は大きいですが、$ 2の列に数字はありません。この場合、このような行($3より大きいが)エラー値は$2なので無視する必要があります。

これを説明するために、上記のサンプルファイルを使用して、file2の「test09」行の2番目の列を「xxxxx」に置き換え、次の結果が表示されます。

$ 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

次に、file3の「test09」行の$2値も数値ではなく値に変更しました。

$ 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

正常に動作しているようですが、コードの2番目の「if」について説明してもよろしいですか?必要な条件もありますか$2~/^[0-9]+/

{ if (($1 in a) && (a[$1] > $3))

ベストアンサー1

最適化awk解決策はおおよそ27数倍速くなる:

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

出力:

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

実行時間の比較:

$ 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

おすすめ記事