Grep большой список против большого файла

В настоящее время я пытаюсь grep добавить большой список идентификаторов (~ 5000) к еще большему файлу csv (3.000.000 строк).

Я хочу, чтобы все строки csv содержали идентификатор из файла id.

Мой наивный подход:

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

Но это берет навсегда!

Существуют ли более эффективные подходы к этой проблеме?

Ответ 1

Try

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.)

Ответ 2

Используйте grep -f для этого:

grep -f the_ids.txt huge.csv > output_file

От man grep:

-f FILE, --file = FILE

Получить шаблоны из FILE, по одному на строку. Пустой файл содержит нуль шаблонов и, следовательно, ничего не соответствует. (-f указан POSIX.)

Если вы предоставите некоторый ввод проб, возможно, мы даже немного улучшим условие grep.

Test

$ 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

Ответ 3

grep -f становится неуправляемым с большими файлами. Даже при использовании grep -f нам нужно помнить несколько вещей:

  • используйте опцию -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 в этом примере):

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 в этом примере):

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