Bash - 別のファイルにリストされている1つのファイルのIDペアを見つける

Bash - 別のファイルにリストされている1つのファイルのIDペアを見つける

IDペアを一覧表示する大容量ファイル「F1」があります。

id1 = 数値、id2 = 文字

id1 id2 id1 id2 ...

目的は、2番目のファイル「F2」から次の形式でIDペアを含む行を抽出することです。

id1 TYHYU 61728497 rtyheyia id2 8372819203948 id1 UJLJF 57383930 utkjruak id2 5683903048377 ...

これを処理するために全能の「grep」を使用しようとしましたが、いくつかの障害に直面しました。

F1の各Id1とId2はF2で何度も繰り返されるため、使用されなくなりましたgrep -Fwf F1.txt F2.txt > F3.txt。つまり、ID1+ID2 は完全な識別子を表します。

実行するとgrep -w "$id1.*$id2" db.txt問題は解決しますが、どのように到達するのかわかりません。 (おそらくwhile read -rgrepを実行する前に、F1の各行を変数セットとして扱うために厄介なループを実行した可能性があります。)

答え1

ほぼすべて来ました。この場合、読み込みループは正常に動作するため、次のようになります。

while read -r line; do
    id1=$(echo "$line" | cut -d ' ' -f 1)
    id2=$(echo "$line" | cut -d ' ' -f 2)
    grep -w "${id1}.*${id2}" "$F2"
done < "$F1"

ただし、探しているIDがデータの他のフィールドのどこにでも存在する場合は、誤検出が返されることがあります。 F2のIDフィールドが常に1列と5列に表示されることを保証できる場合は、フィールドの位置も確認することをお勧めします。これはawk次の行を使用してすばやく実行できますgrep

awk -v id1="$id1" -v id2="$id2" '$1 == id1 && $5 == id2 {print $0}' "$F2"

これはおおよそ「F2の各行について、列1がid1で、列5がid2の場合、行全体を印刷します」と言います。

免責事項、私はこれをテストしていません。

答え2

@John Moonのソリューションは価値があり、これに投票しました。しかし、あなたのファイルが「大きい」と説明されていることを確認しました。ベースのソリューションを使用するには、awkN行の大容量ファイルf1を完全に通過する必要があります。次に、大きな(?)ファイルf2を完全にナビゲートするのにN回かかります。

はいいいえ専門家awk。誰かがf1ファイルを介してIDを収集し、f2ファイルを介して一致を印刷できます。

これは最高のソリューションと同じくらい速く、ややgrep薄暗いソリューションです。awk

まず、f1.txtファイル(N行長)を正規表現ファイル(やはりN行長)に変換します。

$ while read id1 id2; do
   printf '^%s[[:blank:]]+' "$id1"
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '%s[[:blank:]]\n' "$id2"
done < f1.txt > regexp.txt

その醜いprintfシーケンスは、行の先頭で文字列の一致を強制し(列1と一致するために)空白を強制し、3つの(空白ではない)文字列のペア(列1を無視)、列2を繰り返します。します。 , 3, 4) 次に、列 5 の他の文字列とその後にスペースがあります。

したがって、N行の巡回で同じIDペアを含むf1.txt行に一致するN正規表現のリストを作成します。f2.txtリストはに保存されますregexp.txt

f2.txt次のように、一致する行を一度に見つけることができます。

$ egrep -f regexp.txt f2.txt

全体として、スクリプトは次のようになります。

$ while read id1 id2; do
   printf '^%s[[:blank:]]+' "$id1"
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '%s[[:blank:]]\n' "$id2"
done < f1.txt > regexp.txt
$ egrep -f regexp.txt f2.txt

サンプル:

f1.txt:

id1 id2
id1 id2
id3 id4
id3 id5
id4 id5
id4 id6

f2.txt:

id1 TYHYU 61728497 rtyheyia id2 8372819203948
id1 UJLJF 57383930 utkjruak id2 5683903048377
id1 UJLJF 57383930 utkjruak id2 5683903048377
id3 THREE 4444444 adfhdd id4 182i3746
id2 NOPE 4444444 adfhdd id4 182i3746
id3 TREEE 555555 affff id5 8435987345
id4 FOUR  555055 asdfl id5 3728462
id4 FORE  6666666 dfiuyd id6 845687234

中間ファイル regexp.txt (スクリプトで生成):

^id1[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id2[[:blank:]]
^id1[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id2[[:blank:]]
^id3[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id4[[:blank:]]
^id3[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id5[[:blank:]]
^id4[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id5[[:blank:]]
^id4[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id6[[:blank:]]

結果 egrep 出力:

$ egrep -f regexp.txt f2.txt 
id1 TYHYU 61728497 rtyheyia id2 8372819203948
id1 UJLJF 57383930 utkjruak id2 5683903048377
id1 UJLJF 57383930 utkjruak id2 5683903048377
id3 THREE 4444444 adfhdd id4 182i3746
id3 TREEE 555555 affff id5 8435987345
id4 FOUR  555055 asdfl id5 3728462
id4 FORE  6666666 dfiuyd id6 845687234

繰り返しますが、純粋なawkソリューションはより速くエレガントになります。また、私が説明したアプローチは、grepパターンの数が多すぎるとメモリ不足を引き起こす可能性があります。regexp.txtしかし、私はスピード最適化ベースのソリューションになると思いましたgrep

関連情報