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.

107

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

152
répondu The Archetypal Paul 2010-12-06 12:53:24

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?

Voir aussi: https://unix.stackexchange.com/questions/28158/is-there-a-tool-to-get-the-lines-in-one-file-that-are-not-in-another

44

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 ...
32
répondu karakfa 2017-11-02 14:21:13

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)
16
répondu Dennis Williamson 2010-12-06 16:37:44

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

5
répondu aec 2012-08-22 11:24:18

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.)

3
répondu peak 2015-12-29 05:26:52

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)
'
1
répondu HelloGoodbye 2017-08-10 07:49:59

vous pouvez utiliser - diff fileA fileB | grep "^>" | cut -c3- > fileA

Cela fonctionne pour les fichiers qui ne sont pas triées.

0
répondu Darpan 2018-03-30 10:33:18