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?
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.)
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
.
$ 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
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:
-x
option s'il est nécessaire de faire correspondre la ligne entière dans le deuxième fichier-F
si le premier fichier a des chaînes, pas des motifs-w
pour empêcher les correspondances partielles sans utiliser le -x
optionCe 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