J'utilise beaucoup de grep awk sort dans mon shell unix pour travailler avec des fichiers texte de colonne séparés par des tabulations de taille moyenne (environ 10 à 100 millions de lignes). À cet égard, le shell Unix est ma feuille de calcul.
Mais j'ai un gros problème, c'est de sélectionner des enregistrements avec une liste d'identifiants.
Avoir table.csv
fichier au format idtfootbar...
et ids.csv
fichier avec la liste des identifiants, sélectionnez uniquement les enregistrements de table.csv
avec l'identifiant présent dans ids.csv
.
genre de https://stackoverflow.com/questions/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids mais avec shell, pas perl.
grep -F
produit évidemment des faux positifs si les identifiants sont de largeur variable.join
est un utilitaire que je ne pourrais jamais comprendre. Tout d'abord, cela nécessite un tri alphabétique (mes fichiers sont généralement triés par ordre numérique), mais même dans ce cas, je ne peux pas le faire fonctionner sans me plaindre d'un ordre incorrect et sauter certains enregistrements. Donc je n'aime pas ça.
grep -f contre le fichier avec ^idt
-s est très lent lorsque le nombre d'identifiants est important.awk
est encombrant.
Existe-t-il de bonnes solutions pour cela? Des outils spécifiques pour les fichiers séparés par des tabulations ? Des fonctionnalités supplémentaires seront également les bienvenues.
UPD :sort
corrigé -> join
Réponse acceptée :
Je suppose que vous vouliez dire grep -f
pas grep -F
mais vous avez en fait besoin d'une combinaison des deux et de -w
:
grep -Fwf ids.csv table.csv
La raison pour laquelle vous obteniez des faux positifs est (je suppose que vous n'avez pas expliqué) parce que si un identifiant peut être contenu dans un autre, alors les deux seront imprimés. -w
supprime ce problème et -F
s'assure que vos modèles sont traités comme des chaînes et non comme des expressions régulières. Depuis man 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.)
-w, --word-regexp
Select only those lines containing matches that form whole
words. The test is that the matching substring must either be
at the beginning of the line, or preceded by a non-word
constituent character. Similarly, it must be either at the end
of the line or followed by a non-word constituent character.
Word-constituent characters are letters, digits, and the
underscore.
-f FILE, --file=FILE
Obtain patterns from FILE, one per line. The empty file
contains zero patterns, and therefore matches nothing. (-f is
specified by POSIX.)
Si vos faux positifs sont dus au fait qu'un ID peut être présent dans un champ non-ID, parcourez plutôt votre fichier :
while read pat; do grep -w "^$pat" table.csv; done < ids.csv
ou, plus rapide :
xargs -I {} grep "^{}" table.csv < ids.csv
Personnellement, je ferais cela en perl
cependant :
perl -lane 'BEGIN{open(A,"ids.csv"); while(<A>){chomp; $k{$_}++}}
print $_ if defined($k{$F[0]}); ' table.csv