grep a large list against a large file

Obecnie próbuję grep uzyskać dużą listę identyfikatorów (~5000) w stosunku do jeszcze większego pliku csv (3.000.000 linii).

Chcę wszystkie linie csv, które zawierają id z pliku id.

Moje naiwne podejście było:

cat the_ids.txt | while read line
do
  cat huge.csv | grep $line >> output_file
done
Ale to trwa wieczność!

Czy istnieją bardziej efektywne podejścia do tego problemu?

Author: codeforester, 2013-10-15

3 answers

Spróbuj

grep -f the_ids.txt huge.csv

Dodatkowo, ponieważ twoje wzorce wydają się być stałymi ciągami, podanie opcji -F może przyspieszyć 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.)
 19
Author: devnull,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-15 12:14:23

Użyj grep -f do tego:

grep -f the_ids.txt huge.csv > output_file

From man grep:

- f Plik, --file=Plik

Uzyskaj wzorce z pliku, po jednym na linię. Pusty plik zawiera zero wzory, a zatem nic nie pasuje. (- f jest określone przez POSIX.)

Jeśli podasz przykładowe dane wejściowe, może uda nam się nawet poprawić stan grep trochę bardziej.

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
 15
Author: fedorqui,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-15 12:13:35

grep -f filter.txt data.txt staje się niesforny, gdy filter.txt jest większy niż kilka tysięcy linii i dlatego nie jest najlepszym wyborem dla takiej sytuacji. Nawet podczas używania grep -f, musimy pamiętać o kilku rzeczach:

  • użyj opcji -x, jeśli istnieje potrzeba dopasowania całej linii w drugim pliku
  • użyj -F jeśli pierwszy plik ma ciągi znaków, a nie wzorce
  • użyj -w, aby zapobiec częściowemu dopasowaniu, nie używając opcji -x

This post has a great discussion w tym temacie ({[6] } na dużych plikach):

A ten post mówi o grep -vf:


Podsumowując, najlepszym sposobem obsługi grep -f na dużych plikach jest:

Dopasowanie całej linii:

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

Dopasowanie określonego pola w drugim pliku (użycie ogranicznika ',' i pola 2 w ten przykład):

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

Oraz dla grep -vf:

Dopasowanie całej linii:

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

Dopasowanie określonego pola w drugim pliku (użycie ogranicznika ',' i pola 2 w tym przykładzie):

awk -F, 'FNR==NR {hash[$0]; next} !($2 in hash)' filter.txt data.txt > not_matching.txt
 3
Author: codeforester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-15 13:46:42