J'ai un gros fichier A (composé de courriels), une ligne pour chaque courrier. J'ai aussi un autre fichier B qui contient un autre ensemble de mails.
Quelle commande utiliserais-je pour supprimer toutes les adresses figurant dans le fichier B du fichier A.
Donc, si le fichier A contenait:
A
B
C
et le fichier B contenait:
B
D
E
Ensuite, le fichier A devrait être laissé avec:
A
C
Maintenant, je sais que c’est une question qui aurait pu être posée plus souvent, mais j’ai seulement trouvé ne commande en ligne qui m’a donné une erreur avec un mauvais délimiteur.
Toute aide serait très appréciée! Quelqu'un créera sûrement une ligne intelligente, mais je ne suis pas l'expert de Shell.
Si les fichiers sont triés (ils sont dans votre exemple):
comm -23 file1 file2
-23
supprime les lignes qui se trouvent dans les deux fichiers ou uniquement dans le fichier 2. Si les fichiers ne sont pas triés, dirigez-les vers sort
en premier ...
Voir le page de manuel ici
grep -Fvxf <lines-to-remove> <all-lines>
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
: utilise des chaînes littérales au lieu du BRE par défaut-x
: ne considère que les correspondances qui correspondent à toute la ligne-v
: impression non concordante-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 compte également, 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 d'abord fournir fileB.
awk 'NR==FNR{a[$0];next} !($0 in a)' fileB fileA
résultats
A
C
Comment ça marche?
L'argument
NR==FNR{a[$0];next}
Sert à stocker le premier fichier d'un tableau associatif en tant que clés pour un test "contient" ultérieur.
NR==FNR
Vérifie si nous analysons le premier fichier, où le compteur de lignes global (NR) est égal au compteur de lignes de fichiers actuel (FNR).
a[$0]
Ajoute la ligne actuelle au tableau associatif en tant que clé. Notez que cela se comporte comme un ensemble, dans lequel il n'y aura pas de valeurs dupliquées (clés).
!($0 in a)
nous sommes dans le (s) prochain (s) fichier (s),in
est un test contient, ici, il vérifie si la ligne en cours est dans l'ensemble que nous avons rempli dans la première étape à partir du premier fichier ,!
Annule la condition. Ce qui manque ici, c'est l'action, qui par défaut est{print}
Et qui n'est généralement pas écrite explicitement.
Notez que cela peut maintenant être utilisé pour supprimer des mots de la liste noire.
$ awk '...' badwords allwords > goodwords
avec une légère modification, il peut nettoyer plusieurs listes et créer des versions nettoyées.
$ awk 'NR==FNR{a[$0];next} !($0 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 soit imprimée exactement.
man diff
pour plus de détails
Ce raffinement de la réponse Nice de @ karakfa pourrait être nettement plus rapide pour les très gros fichiers. Comme pour cette réponse, aucun fichier n'a besoin d'être trié, mais la rapidité est assurée par les tableaux associatifs de awk. Seul le fichier de recherche est conservé en mémoire.
Cette formulation permet également de n’utiliser qu’un seul champ ($ N) dans le fichier d’entrée 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[$0]=$0 } }
!($N in dictionary) {print}'
(Un autre avantage de cette approche réside dans le fait qu’il est facile de modifier le critère de comparaison, par exemple, de supprimer les espaces blancs de début et de fin.)
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 fonctionnera pour les fichiers qui ne sont pas triés également.