web-dev-qa-db-ja.com

大きなファイルに対して大きなリストをgrep

現在、さらに大きなcsvファイル(3.000.000行)に対して、IDの大きなリスト(〜5000)をgrepしようとしています。

Idファイルのidを含むすべてのcsv行が必要です。

私の素朴なアプローチは:

cat the_ids.txt | while read line
do
  cat huge.csv | grep $line >> output_file
done

しかし、これには永遠に時間がかかります!

この問題に対するより効率的なアプローチはありますか?

23
leifg

試して

grep -f the_ids.txt huge.csv

さらに、パターンは固定文字列のように見えるため、-Fオプションはgrepを高速化するかもしれません。

   -F, --fixed-strings
          Interpret PATTERN as a  list  of  fixed  strings,  separated  by
          newlines,  any  of  which is to be matched.  (-F is specified by
          POSIX.)
30
devnull

これにはgrep -fを使用します。

grep -f the_ids.txt huge.csv > output_file

man grepから:

-f FILE、-file = FILE

FILEからパターンを1行に1つずつ取得します。空のファイルにはパターンが含まれていないため、何にも一致しません。 (-fはPOSIXで指定されます。)

サンプル入力を提供する場合は、grep条件をもう少し改善することもできます。

テスト

$ cat ids
11
23
55
$ cat huge.csv 
hello this is 11 but
nothing else here
and here 23
bye

$ grep -f ids huge.csv 
hello this is 11 but
and here 23
16
fedorqui

grep -f filter.txt data.txtは、filter.txtが数千行よりも大きい場合に手に負えなくなるため、このような状況には最適な選択肢ではありません。 grep -fを使用している場合でも、いくつかの点に留意する必要があります。

  • 2番目のファイルの行全体を一致させる必要がある場合は、-xオプションを使用します
  • 最初のファイルにパターンではなく文字列がある場合は、-Fを使用します
  • -wオプションを使用していないときに部分一致を防ぐには、-xを使用します

この投稿には、このトピックに関する素晴らしい議論があります(大きなファイルのgrep -f):

そして、この投稿はgrep -vfについて語っています:


要約すると、大きなファイルでgrep -fを処理する最良の方法は次のとおりです:

行全体に一致:

awk 'FNR==NR {hash[$0]; next} $0 in hash' filter.txt data.txt > matching.txt

2番目のファイル内の特定のフィールドを一致させる(この例では「、」区切り文字とフィールド2を使用):

awk -F, 'FNR==NR {hash[$1]; next} $2 in hash' filter.txt data.txt > matching.txt

およびgrep -vfの場合:

行全体に一致:

awk 'FNR==NR {hash[$0]; next} !($0 in hash)' filter.txt data.txt > not_matching.txt

2番目のファイル内の特定のフィールドを一致させる(この例では「、」区切り文字とフィールド2を使用):

awk -F, 'FNR==NR {hash[$0]; next} !($2 in hash)' filter.txt data.txt > not_matching.txt
4
codeforester