web-dev-qa-db-ja.com

異なるファイルの2つの列を比較し、一致する場合は出力する

Solaris 10を使用しているため、-fを含むgrepオプションが機能しません。

パイプで区切られた2つのファイルがあります。

file1:

abc|123|BNY|Apple|
cab|234|cyx|orange|
def|kumar|pki|bird|

ファイル2:

abc|123|
kumar|pki|
cab|234

File2の最初の2つの列をfile1と比較します(最初の2列でfile1のコンテンツ全体を検索)。次に、ファイル2の2行目などを検索します。

期待される出力:

abc|123|BNY|Apple|
cab|234|cyx|orange|

私が持っているファイルは巨大で、約400,000行を含んでいるので、実行を速くしたいと思います。

16
user68365

これは、awkが以下のために設計されたものです。

$ awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0' file2 file1
abc|123|BNY|Apple|
cab|234|cyx|orange|

説明

  • -F'|':フィールド区切り文字を|に設定します。
  • NR==FNR:NRは現在の入力行番号、FNRは現在のファイルの行番号です。 2つは、最初のファイルが読み取られている間だけ等しくなります。
  • c[$1$2]++; next:これが最初のファイルの場合、最初の2つのフィールドをc配列に保存します。次に、次の行にスキップして、これが最初のファイルにのみ適用されるようにします。

  • c[$1$2]>0:elseブロックは、これが2番目のファイルである場合にのみ実行されるため、このファイルのフィールド1と2がすでに表示されているかどうかを確認し(c[$1$2]>0)、表示されている場合は、ライン。 awkでは、デフォルトのアクションは行を印刷することなので、c[$1$2]>0がtrueの場合、行が印刷されます。


あるいは、Perlでタグ付けしたので:

Perl -e 'open(A, "file2"); while(<A>){/.+?\|[^|]+/ && $k{$&}++};
         while(<>){/.+?\|[^|]+/ && do{print if defined($k{$&})}}' file1

説明

最初の行はfile2を開き、2番目の|.+?\|[^|]+)までをすべて読み取り、それを保存します($&は最後の一致演算子の結果です)。 %kハッシュ。

2行目はfile1を処理し、同じ正規表現を使用して最初の2つの列を抽出し、それらの列が%kハッシュで定義されている場合は行を出力します。


上記のアプローチはどちらも、file2の最初の2列をメモリに保持する必要があります。数十万行しかない場合は問題になりませんが、もしそうであれば、次のようなことができます。

cut -d'|' -f 1,2 file2 | while read pat; do grep "^$pat" file1; done

しかし、それは遅くなります。

21
terdon

SQLのような方法で問題を考えたい場合は、必ず ' q 'という名前のツールを試してください。

$ q -d '|' "select f1.* from file1 f1 join file2 f2 on (f1.c1 = f2.c1 and f1.c2 = f2.c2)"

SQLクエリに慣れていると、より明確で理解しやすくなります。

1
Vincent

おもう

grep -Ff file2 file1

あなたが探しているものです。それは効率的であるべきですが、あなたが望むほど正確であるかどうかはわかりません。 abc|123(たとえば)が異なる列のfile1の行にある場合、その行も出力されます。これが絶対に起こらないことを保証できれば、上記の行が機能するはずです。

1
Joseph R.
$  sed 's/^/\^/' 2.txt > temp.txt ; grep 1.txt -f temp.txt
abc|123|BNY|Apple|
cab|234|cyx|orange|
0
mr_tron