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


16

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の内容全体を検索する)場合、file1の一致する行を印刷します。次に、ファイル2の2行目などを検索します。

期待される出力:

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

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


例から先頭のスペースを削除しました。必要に応じて、編集をロールバックしてください。スペースは重要であることに注意してください。実際のファイルに存在する場合にのみスペースを使用してください。
テルドン

GNUバージョンを使用してみてくださいgrep、それは下です、/usr/sfw/bin/ggrepstackoverflow.com/questions/15259882/...
SLM

回答:


21

これが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:これが最初のファイルの場合、c配列の最初の2つのフィールドを保存します。次に、次の行にスキップして、これが最初のファイルにのみ適用されるようにします。

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


あるいは、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

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


しかし、これはすべて(最初の2列)file2をメモリにロードしませんか?
ジョセフR. 14

@terdon:awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0'短縮版です。
cuonglm 14

動作し
ません

@ user68365:file2行が重複していますか?
cuonglm 14

NOそれはどんな重複行していdoes notの
user68365

1

おもう

grep -Ff file2 file1

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


abc | 123はファイルのどこかに存在する可能性があるため、Grepでは十分ではありません。さらに、私はsolaris 10を使用していますが、そのgrepオプションも使用できません。
user68365 14

2
@ user68365はあなたの質問でこれをすべて明確にしてください。OSを教えて、最初の2列のみを一致させることを指定する必要があります。
テルドン

1

SQLの問題を方法と同様に考えたい場合は、 ' q ' という名前のツールを試してください。

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

SQLクエリに精通している場合は、より明確で理解しやすいです。


最小の謎めいた解決策の1つをありがとう。それが私が欲しいものです。しかし、この「qツール」を
ロルフ

非常に便利なツール。
ghilesZ

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

1
私が質問で編集して言及したように、grep -fオプションは私のシステムで動作し
ません

Solaris 10の/ usr / sfw / binにgnu core-utilsがあります/ usr / sfw / bin / sedおよび/ usr / sfw / bin / grepを使用してください
mr_tron 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.