web-dev-qa-db-fra.com

Différence de deux gros fichiers

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

à présent

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 quelque chose d'autre, les deux commandes ci-dessus afficheront 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, ainsi 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 -q et diff -s mais ils ne l'ont pas fait. L'ordre n'a pas d'importance, ce qui compte, c'est que je ne souhaite voir que la différence, ni> ni <ni un espace.

grep -FvF a fait le tour des petits fichiers, pas des gros

le premier fichier contient plus de 5 millions de lignes, le second 1300.

donc results.csv devrait aboutir à environ 4 998 700 lignes

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

14
Lynob

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)

Donc, le -3 signifie que seules les lignes uniques à l'un des fichiers seront imprimées. Cependant, ceux-ci sont mis en retrait en fonction du 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
20
terdon

Utilisation de grepavec bashname__:

$ 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 le résultat sous results.csv:

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

  • 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 par catname__

Ou comme Oli a suggéré , vous pouvez également utiliser le groupement 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 alors, courez les uns après les autres, 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
6
heemayl

Utilisez les options --*-line-format=... de diffname__

Vous pouvez indiquer à diffexactement ce dont vous avez besoin - comme 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 de manière très détaillée, semblable à un format de numéro printfname__.

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

Les options dont nous avons besoin sont celles qui permettent 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 ne voulons afficher que le texte des lignes. %L est le symbole de format pour le texte de la ligne.
Pour les lignes inchangées, nous ne voulons rien afficher.

Avec cela, nous pouvons écrire des options comme --old-line-format='%L', et les mettre ensemble, 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 la performance

Étant donné 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 diffpuisse gérer une manière mieux que l'autre. Mieux vaut avoir moins de mémoire ou moins de calculs.

Il existe une option d'optimisation pour utiliser diffavec 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, mais cela vaut la peine de l'essayer.

Les options de format sont décrites dans le man diff sous --LTYPE-line-format=LFMT.

4
Volker Siegel

Si l'ordre des lignes n'est pas pertinent, utilisez awk ou Perl:

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

Utilisez grep pour 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.

4
muru

Puisque la commande n'a pas besoin d'être préservée, simplement:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: fusionne et trie test1.csv et test2.csv
  • uniq -u: imprime uniquement les lignes sans doublon
3
kos