Différence de deux gros fichiers

14

J'ai "test1.csv" et il contient

200,400,600,800
100,300,500,700
50,25,125,310

et test2.csv et il contient

100,4,2,1,7
200,400,600,800
21,22,23,24,25
50,25,125,310
50,25,700,5

maintenant

diff test2.csv test1.csv > result.csv

est différent de

diff test1.csv test2.csv > result.csv

Je ne sais pas quel est le bon ordre mais je veux autre chose, les deux commandes ci-dessus produiront quelque chose comme

2 > 100,4,2,1,7
   3 2,3c3,5
   4 < 100,300,500,700
   5 < 50,25,125,310
   6 \ No newline at end of file
   7 ---
   8 > 21,22,23,24,25
   9 > 50,25,125,310

Je veux afficher uniquement la différence, donc results.csv devrait ressembler à ceci

100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

J'ai essayé diff -qet diff -smais ils n'ont pas fait l'affaire. L'ordre n'a pas d'importance, ce qui compte, c'est que je ne veux voir que la différence, pas de> ni <ni d'espace vide.

grep -FvF a fait l'affaire sur les petits fichiers pas sur les gros

le premier fichier contient plus de 5 millions de lignes, le second fichier en contient 1 300.

donc results.csv devrait donner environ 4 998 700 lignes

J'ai également essayé grep -F -x -v -f ce qui n'a pas fonctionné.

Lynob
la source
1
@Tim, j'ai vu votre lien et je suis un ancien membre, donc je connais les règles, mais j'étais imprudent, désolé :) le modifiais, et j'ai vu une fenêtre contextuelle indiquant que le message a été modifié, alors vous avez fait le travail pour moi et je suis reconnaissant monsieur.
Lynob
50,25,125,310est commun aux deux fichiers .. vous devez supprimer cela de votre sortie souhaitée ..
heemayl
Faut-il conserver l'ordre?
kos
1
dépend en quelque sorte de ce que vous voulez faire avec les informations, diff, IMO, sert à créer un patch. En tout cas, je suis maintenant sûr de votre meilleur outil, diff, grep, awk ou perl.
Panther

Réponses:

20

Cela ressemble à un travail pour comm:

$ comm -3 <(sort test1.csv) <(sort test2.csv)
100,300,500,700
    100,4,2,1,7
    21,22,23,24,25
    50,25,700,5

Comme expliqué dans man comm:

   -1     suppress column 1 (lines unique to FILE1)

   -2     suppress column 2 (lines unique to FILE2)

   -3     suppress column 3 (lines that appear in both files)

Ainsi, -3cela signifie que seules les lignes uniques à l'un des fichiers seront imprimées. Cependant, ceux-ci sont en retrait selon le fichier dans lequel ils ont été trouvés. Pour supprimer l'onglet, utilisez:

$ comm -3 <(sort test1.csv) <(sort test2.csv) | tr -d '\t'
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Dans ce cas, vous n'avez même pas vraiment besoin de trier les fichiers et vous pouvez simplifier ce qui précède pour:

comm -3 test1.csv test2.csv | tr -d '\t' > difference.csv
terdon
la source
Vous n'avez pas été dupé par les espaces après la 200,[...]ligne hein? :)
kos
@kos non, j'ai d'abord supprimé les espaces de fin des fichiers. J'ai supposé que les fichiers de l'OP n'en avaient pas réellement.
terdon
6

Utilisation grepavec bashsubstitution de processus:

$ cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv)
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Pour enregistrer la sortie sous results.csv:

cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv) >results.csv
  • <()est le bashmodèle de substitution de processus

  • grep -vFf test2.csv test1.csv trouvera les lignes uniques à seulement test1.csv

  • grep -vFf test1.csv test2.csv trouvera les lignes uniques à seulement test2.csv

  • Enfin, nous résumons les résultats en cat

Ou, comme l'a suggéré Oli , vous pouvez également utiliser le regroupement de commandes:

$ { grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv; }
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Ou exécutez-les simplement l'un après l'autre, car ils écrivent tous les deux sur STDOUT, ils seront finalement ajoutés:

$ grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5
heemayl
la source
1
Pourquoi catdeux commandes redirigées? Pourquoi ne pas simplement exécuter l'un puis l'autre? grep ... ; grep ...ou { grep ... ; grep ... ; }si vous vouliez faire quelque chose avec la sortie collective.
Oli
@Oli Merci .. c'est une excellente idée .. je n'y ai pas pensé ..
heemayl
4

Si l'ordre des lignes n'est pas pertinent, utilisez awkou perl:

awk '{seen[$0]++} END {for (i in seen) {if (seen[i] == 1) {print i}}}' 1.csv 2.csv

Utilisez greppour obtenir les lignes communes et les filtrer:

grep -hxvFf <(grep -Fxf 1.csv 2.csv) 1.csv 2.csv

Le grep interne obtient les lignes communes, puis le grep externe trouve les lignes qui ne correspondent pas à ces lignes communes.

muru
la source
Votre commande awk réimplémente simplement sort | uniq -u, ce qui donne la mauvaise réponse lorsqu'un fichier contient des lignes en double. Pour grep, je dirais "intérieur" / "extérieur", pas "interne" / "externe".
Peter Cordes
@PeterCordes oui, c'est le cas et qui êtes-vous pour dire que c'est le mauvais résultat?
muru
Faux en ce sens que ce n'est pas exactement ce que la question demandait, dans ce cas d'angle. Il est peut - être ce que quelqu'un veut, mais vous devez indiquer la différence entre ce que votre awkimprimera et ce que le comm -3et les diffréponses seront imprimées.
Peter Cordes
@PeterCordes encore, qui êtes-vous pour dire ça? Jusqu'à ce que l'OP dise que c'est ce qu'ils veulent, peu m'importe si la sortie diffère de celle de comm -3. Je ne vois aucune raison pour laquelle je devrais expliquer cela. Si vous souhaitez modifier une note, n'hésitez pas.
muru
L'OP a dit qu'il voulait la différence. Ce n'est pas toujours ce que produit votre programme. Un programme qui produit la même sortie pour un cas de test, mais qui ne satisfait pas la description telle qu'elle est écrite pour tous les cas, nécessite un avertissement. Je suis ici pour le dire, et c'est vrai peu importe qui je suis ou qui vous êtes. J'ai ajouté une note.
Peter Cordes
4

Utilisez les --*-line-format=...options dediff

Vous pouvez dire diffexactement ce dont vous avez besoin - expliqué ci-dessous:

diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' f1.txt f2.txt

Il est possible de spécifier la sortie de diff d'une manière très détaillée, semblable à un printf format numérique.

Les lignes du premier fichier, test1.csvsont appelées "anciennes" lignes, et les lignes du second test2.csv, sont des "nouvelles" lignes. Cela a du sens quand diffest utilisé pour voir ce qui a changé dans un fichier.

Les options dont nous avons besoin sont celles permettant de définir le format des "anciennes" lignes, des "nouvelles" lignes et des lignes "inchangées".
Les formats dont nous avons besoin sont très simples:
pour les lignes modifiées, nouvelles et anciennes, nous voulons afficher uniquement le texte des lignes. %Lest le symbole de format du texte de la ligne.
Pour les lignes inchangées, nous ne voulons rien montrer.

Avec cela, nous pouvons écrire des options comme --old-line-format='%L' , et tout assembler, en utilisant vos données d'exemple:

$ diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' test1.csv test2.csv
100,4,2,1,7
100,300,500,700
21,22,23,24,25
50,25,700,5


Notes sur les performances

Parce que les fichiers ont une taille différente, essayez d'échanger les fichiers d'entrée si cela n'a pas d'importance, il se peut que le fonctionnement interne de diff puisse gérer une manière mieux que l'autre. Mieux vaut soit avoir besoin de moins de mémoire, soit moins de calculs.

Il existe une option d'optimisation pour l'utilisation diff avec des fichiers volumineux: --speed-large-files. Il utilise des hypothèses sur la structure du fichier, il n'est donc pas clair si cela vous aide dans votre cas, mais cela vaut la peine de l'essayer.

Les options de format sont décrites ci- man diffdessous --LTYPE-line-format=LFMT.

Volker Siegel
la source
3

Comme l'ordre n'a pas besoin d'être conservé, il suffit de:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: fusionne et trie test1.csvettest2.csv
  • uniq -u: imprime uniquement les lignes sans doublon
kos
la source
Cela ne fonctionne pas si un fichier contient une ligne deux fois, cela n'apparaît pas dans l'autre fichier. Les deux occurrences seraient un diffrésultat.
Volker Siegel