web-dev-qa-db-fra.com

grep une grande liste contre un gros fichier

J'essaie actuellement de grep une grande liste d'identifiants (~ 5000) contre un fichier csv encore plus grand (3.000.000 lignes).

Je veux toutes les lignes csv, qui contiennent un id du fichier id.

Mon approche naïve était:

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

Mais cela prend une éternité!

Existe-t-il des approches plus efficaces à ce problème?

23
leifg

Essayer

grep -f the_ids.txt huge.csv

De plus, étant donné que vos modèles semblent être des chaînes fixes, fournir le -F l'option pourrait accélérer 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

Utilisation grep -f pour ça:

grep -f the_ids.txt huge.csv > output_file

De man grep:

-f FILE, --file = FILE

Obtenez des modèles de FILE, un par ligne. Le fichier vide ne contient aucun motif et ne correspond donc à rien. (-f est spécifié par POSIX.)

Si vous fournissez un exemple d'entrée, nous pouvons peut-être même améliorer un peu plus la condition grep.

Tester

$ 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 devient indiscipliné lorsque filter.txt est plus grand que quelques milliers de lignes et n'est donc pas le meilleur choix pour une telle situation. Même en utilisant grep -f, nous devons garder quelques points à l'esprit:

  • utilisation -x option s'il est nécessaire de faire correspondre la ligne entière dans le deuxième fichier
  • utilisation -F si le premier fichier a des chaînes, pas des motifs
  • utilisation -w pour empêcher les correspondances partielles sans utiliser le -x option

Ce message a une grande discussion sur ce sujet (grep -f sur des fichiers volumineux):

Et ce post parle de grep -vf:


En résumé, la meilleure façon de gérer grep -f sur les gros fichiers est:

Correspondance avec toute la ligne:

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

Faire correspondre un champ particulier dans le deuxième fichier (en utilisant le délimiteur ',' et le champ 2 dans cet exemple):

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

et pour grep -vf:

Correspondance avec toute la ligne:

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

Faire correspondre un champ particulier dans le deuxième fichier (en utilisant le délimiteur ',' et le champ 2 dans cet exemple):

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