Comment supprimer les lignes qui apparaissent sur le fichier B d'un autre fichier A?
j'ai un grand fichier (composé des e-mails), une ligne pour chaque mail. J'ai aussi un autre fichier B qui contient un autre ensemble de mails.
quelle commande devrais-je utiliser pour supprimer toutes les adresses qui apparaissent dans le fichier B du fichier A.
Ainsi, si Un fichier:
A
B
C
et le dossier B contenait:
B
D
E
alors file a devrait à gauche:
A
C
maintenant je sais que c'est une question qui aurait pu être posée plus souvent, mais j'ai seulement trouvé une commande en ligne qui m'a donné une erreur avec un mauvais délimiteur.
toute aide serait très appréciée! Quelqu'un trouvera sûrement un habile doublure, mais je ne suis pas l'expert en coquillages.
8 réponses
comm -23 file1 file2
-23 supprime les lignes qui sont dans les deux fichiers, ou seulement dans le fichier 2. Les fichiers doivent être triés (ils sont dans votre exemple) mais sinon, passez-les d'abord par sort
...
voir la man page Ici
grep -Fvxf <lines-to-remove> <all-lines>
- fonctionne sur la non-trier les fichiers
- maintient l'ordre
- est POSIX
exemple:
cat <<EOF > A
b
1
a
0
01
b
1
EOF
cat <<EOF > B
0
1
EOF
grep -Fvxf B A
sortie:
b
a
01
b
explication:
-
-F
: utilisez des chaînes littérales à la place du BRE par défaut -
-x
: ne considérer que les correspondances qui correspondent à la ligne entière -
-v
: impression de non-correspondance -
-f file
: prendre des motifs du fichier donné
cette méthode est plus lente sur les fichiers pré-triés que les autres méthodes, car elle est plus générale. Si la vitesse de questions, voir: moyen Rapide de trouver des lignes dans un fichier qui ne sont pas dans un autre?
awk à la rescousse!
cette solution ne nécessite pas d'entrées triées. Vous devez fournir fichierb premier.
awk 'NR==FNR{a["151900920"];next} !("151900920" in a)' fileB fileA
retourne
A
C
comment ça marche?
NR==FNR{a["151940920"];next}
l'idiome est pour stocker le premier fichier dans un tableau associatif comme des clés pour plus tard "contient".
NR==FNR
est vérifier si nous numérisons le premier fichier, où le compteur de ligne global (NR) est égal au compteur de ligne de fichier courant (FNR).
a["151960920"]
ajoute la ligne courante au tableau associatif comme clé, notez que cela se comporte comme un ensemble, où il n'y aura pas de valeurs dupliquées (clés)
!("151970920" in a)
nous sommes maintenant dans le(S) fichier (s) suivant (s),in
est un test de contains, ici il s'agit de vérifier si la ligne courante est dans l'ensemble que nous avons peuplé la première étape du premier fichier,!
nie la condition. Ce qui manque ici est l'action, qui par défaut est{print}
et généralement pas écrit explicitement.
notez que cela peut maintenant être utilisé pour supprimer les mots de la liste noire.
$ awk '...' badwords allwords > goodwords
avec un léger changement il peut nettoyer plusieurs listes et créer des versions nettoyées.
$ awk 'NR==FNR{a["151930920"];next} !("151930920" in a){print > FILENAME".clean"}' bad file1 file2 file3 ...
une autre façon de faire la même chose (nécessite également une entrée triée):
join -v 1 fileA fileB
dans Bash, si les fichiers ne sont pas pré-triés:
join -v 1 <(sort fileA) <(sort fileB)
vous pouvez le faire à moins que vos fichiers ne soient triés
diff file-a file-b --new-line-format="" --old-line-format="%L" --unchanged-line-format="" > file-a
--new-line-format
est, pour les lignes qui sont dans le fichier b, mais pas dans un
--old-..
est pour les lignes qui sont dans le fichier a mais pas dans b
--unchanged-..
est, pour les lignes qui sont dans les deux.
%L
fait en sorte que la ligne est imprimée exactement.
man diff
pour plus de détails
ce raffinement de la réponse sympathique de @karakfa peut être nettement plus rapide pour les très gros dossiers. Comme avec cette réponse, aucun fichier n'a besoin d'être trié, mais la vitesse est assurée en vertu des tableaux associatifs d'awk. Seul le fichier de recherche est conservé en mémoire.
cette formulation permet également la possibilité qu'un seul champ particulier ($N) dans le fichier d'entrée soit utilisé dans la comparaison.
# Print lines in the input unless the value in column $N
# appears in a lookup file, $LOOKUP;
# if $N is 0, then the entire line is used for comparison.
awk -v N=$N -v lookup="$LOOKUP" '
BEGIN { while ( getline < lookup ) { dictionary["151900920"]="151900920" } }
!($N in dictionary) {print}'
(un Autre avantage de cette approche est qu'il est facile de modifier le critère de comparaison, par exemple pour couper l'espace blanc menant et arrière.)
vous pouvez utiliser Python:
python -c '
lines_to_remove = set()
with open("file B", "r") as f:
for line in f.readlines():
lines_to_remove.add(line.strip())
with open("file A", "r") as f:
for line in [line.strip() for line in f.readlines()]:
if line not in lines_to_remove:
print(line)
'
vous pouvez utiliser -
diff fileA fileB | grep "^>" | cut -c3- > fileA
Cela fonctionne pour les fichiers qui ne sont pas triées.