Y at-il un outil pour obtenir les lignes dans un fichier qui ne sont pas dans un autre?

Réponses:

159

Oui. L' grepoutil standard de recherche de chaînes de texte dans les fichiers peut être utilisé pour soustraire toutes les lignes d'un fichier à un autre.

grep -F -x -v -f fileB fileA

Cela fonctionne en utilisant chaque ligne de fileB comme un motif ( -f fileB) et en la traitant comme une chaîne simple à faire correspondre (pas une expression régulière régulière) ( -F). Vous forcez la correspondance sur toute la ligne ( -x) et n'imprimez que les lignes qui ne correspondent pas ( -v). Par conséquent, vous imprimez dans le fichier A les lignes qui ne contiennent pas les mêmes données que toutes les lignes du fichier B.

L'inconvénient de cette solution est qu'elle ne prend pas en compte l'ordre des lignes et que, si vous saisissez des lignes en double à différents endroits, vous risquez de ne pas obtenir ce que vous attendiez. La solution à cela consiste à utiliser un véritable outil de comparaison tel que diff. Vous pouvez le faire en créant un fichier diff avec la valeur de contexte à 100% des lignes du fichier, puis en l’analysant uniquement pour les lignes qui seraient supprimées si le fichier A était converti en fichier B. (Remarque: cette commande supprime également le fichier diff. le formatage après avoir obtenu les bonnes lignes.)

diff -U $(wc -l < fileA) fileA fileB | sed -n 's/^-//p' > fileC
Caleb
la source
@ inderpreet99 Le minuscule -uargument ne tient en fait un paramètre d'un nombre aussi longtemps qu'il est pas suivi d'un espace. L'avantage de la façon dont je l'ai eu avant est que cela fonctionnera avec ou sans valeur, vous pouvez donc utiliser quelque chose dans cette routine de sous-commande qui renvoie non-sortie. Les majuscules -U nécessitent par contre un argument.
Caleb
soyez prudent, grep -f est O (N ^ 2) Je crois: stackoverflow.com/questions/4780203/…
rogerdpack
1
le diffpipeline fonctionne un plaisir merci.
Felipe Alvarez
Pour prendre en compte le problème de tri, vous pouvez utiliser la substitution de processus dans la commande pour traiter chaque fichier avant le grepbesoin. Exemple:grep -F -x -v -f <(sort fileB) <(sort fileA)
Tony Cesaro
@TonyCesaro Cela fonctionnerait si votre ensemble de données n'était pas spécifique à une commande et qu'il n'était pas nécessaire de prendre en compte les doublons. L'avantage d'utiliser diffest que cette position dans le fichier est prise en compte.
Caleb
57

La réponse dépend beaucoup du type et du format des fichiers que vous comparez.

Si les fichiers que vous comparez sont des fichiers texte triés, l'outil GNU écrit par Richard Stallman et Davide McKenzie, appelé, commpeut effectuer le filtrage souhaité. Cela fait partie des coreutils.

Exemple

Disons que vous avez les 2 fichiers suivants:

$ cat a
1
2
3
4
5

$ cat b
1
2
3
4
5
6

Lignes dans le fichier bqui ne sont pas dans le fichier a:

$ comm <(sort a) <(sort b) -3
    6
Un ami
la source
1
+1 pour mentionner comm; malheureusement, commnécessite des fichiers triés
Arcege
11
alors les trier? comm <(sorte a) <(sorte b) -1 -2
Sirex
C'est une syntaxe étrange. <()? Cela fonctionne et je comprends, mais y a-t-il un nom pour cette étrangeté?
mlissner
2
@mlissner <()est également appelé substitution de processus .
Miku
1
comma été écrit vers 1973 par une personne des Bell Labs et non par la firme. Vous faites référence à la mise en oeuvre de GNU qui est venue beaucoup plus tard. Il y a eu beaucoup d'implémentations différentes des utilitaires Unix au cours des années.
Stéphane Chazelas
32

de stackoverflow ...

comm -23 fichier1 fichier2

-23 supprime les lignes qui se trouvent dans les deux fichiers, ou uniquement dans le fichier 2. Les fichiers doivent être triés (ils le sont dans votre exemple), sinon, dirigez-les d'abord par tri ...

Voir la page de man ici

JJS
la source
Cela ne fonctionne pas pour moi, pour une raison ...
Jan
@Jan vos fichiers sont-ils triés? Comment les avez-vous triés?
JJS
8

Les méthodes grep et comm (avec sort) prennent beaucoup de temps sur les gros fichiers. SiegeX et ghostdog74 ont partagé deux excellentes méthodes awk pour extraire des lignes uniques à l'un des deux fichiers sur Stack Overflow:

$ awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2

$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
Miles Wolbe
la source
2
Si vous faites cela avec des fichiers volumineux, les contraintes de mémoire pour charger un fichier volumineux dans un tableau associatif vont être prohibitives.
Charles Duffy
4

Si les fichiers sont volumineux et que vos entrées ne sont pas personnalisées, grep prend beaucoup trop de temps. Une alternative rapide serait

sort file1 > 1 
sort file2 > 2 
diff 1 2 | grep "\>" | sed -e 's/> //'

[fichier2-fichier1 résultats à l'écran, pipe à fichier, etc.]

Changer >pour <obtenir la soustraction opposée.rm 1 2

Eshel Faraggi
la source
2

Vous pouvez également considérer vimdiff, il met en évidence les différences entre les fichiers dans un éditeur vim

Simona
la source
1
Mais existe-t-il un moyen facile de faire automatiquement la soustraction dans Vimdiff?
Kazark