trouver des lignes communes dans des fichiers basés sur une colonne

J’ai 15 fichiers comme

fichier1.csv

a,cg2,0,0,0,21,0 a,cq1,10,0,0,0,0 a,cm2,0,19,0,0,0 ... a,ad10,0,0,0,37,0 

fichier2.csv

 d,cm1,0,3,0,0,0 d,cs2,0,32,0,0,0 d,cg2,0,0,9,0,0 ... d,az2,0,0,0,21,0 

. . . .

file15.csv

 s,sd1,0,23,0,0,0 s,cw1,0,0,7,0,0 s,c23,0,0,90,0,0 ... s,cg2,0,45,0,0,0 

J’ai un nombre différent de lignes dans chaque fichier et je veux comparer le deuxième champ de tous les 15 fichiers et extraire les lignes qui sont communes au second champ de tous les 15 fichiers.

dans ce cas ci-dessus

la sortie est:

cg2

(en le prenant est commun au deuxième champ de tous les 15 fichiers)

Je suis un peu nouveau à unix et shell script, s’il vous plaît aider

Voulez-vous les lignes complètes de chacun des quinze fichiers où le champ 2 apparaît dans les quinze fichiers? Ou voulez-vous seulement une liste des valeurs du champ 2 qui apparaissent dans les quinze fichiers.

L’ancien:

 a,cg2,0,0,0,21,0 d,cg2,0,0,9,0,0 . . . s,cg2,0,45,0,0,0 . . . 

Le dernier:

 cg2 . . . 

Si ce dernier, alors cela devrait fonctionner

 awk -F, '{arr[$2]++; if (FILENAME != prevfile) {c++; prevfile = FILENAME}} END {for (i in arr) {if (arr[i] == c) {print i}}}' file*.csv 

Cassé sur plusieurs lignes:

 awk -F, '{ arr[$2]++; if (FILENAME != prevfile) { c++; prevfile = FILENAME } } END { for (i in arr) { if (arr[i] >= c) { print i } } }' file*.csv 

Explication:

  • incrémenter le nombre de fois où une valeur de champ 2 se produit
  • Si le nom de fichier change, incrémentez le nombre de fichiers (le premier fichier passe d’une chaîne nulle à son nom de fichier et le nombre augmente de 0 à 1)
  • enregistrer le nom de fichier actuel
  • une fois tout le comptage effectué, itérer le tableau par ses clés
  • Si le nombre contenu dans le tableau est supérieur ou égal au nombre de fichiers, la valeur du champ 2 apparaît dans tous les fichiers (en recherchant >= au lieu de == cela fonctionnera si une valeur apparaît plus d’une fois dans un seul fichier)
  • imprimez donc la clé (qui est une valeur de champ 2)
  • un glob est utilisé pour obtenir tous les fichiers, mais vous pouvez les lister explicitement

Modifier:

Voici un moyen d’imprimer les lignes de correspondance complètes en utilisant une technique à deux passes. C’est une modification de la version ci-dessus. Assurez-vous de lister les fichiers deux fois.

 awk -F, ' FILENAME == first && flag { exit } ! first { first = FILENAME } FILENAME != first { flag = 1 } { arr[$2]++; if (FILENAME != prevfile) { c++; prevfile = FILENAME } } END { # print the matching lines do { if ($2 in arr) { print; } } while (getline); # print the list of words for (i in arr) { if (arr[i] >= c) { print i } } }' file*.csv file*.csv 

Cela dépend du premier fichier du premier groupe qui porte le même nom que le premier fichier du deuxième groupe. L’utilisation de globulation similaire à ce que j’ai montré se chargera de cette exigence.

Il imprime les lignes correspondantes (mais non groupées), puis imprime la liste des mots. Si vous ne voulez que l’un ou l’autre, commentez ou supprimez la boucle que vous ne voulez pas ( do/while ou for ).

Si vous imprimez uniquement les lignes complètes, vous pouvez canaliser la sortie vers:

 sort -t , -k2,2 

les regrouper.

Ne canalisant que la liste de mots pour:

 sort 

les mettra dans le même ordre pour faciliter la comparaison.

Problème amusant

Une façon de le faire, entièrement en Bash, est la suivante.

Une chose que vous devrez invoquer est join -t ',' -1 2 -2 2 file1 file2 pour vous joindre à la deuxième colonne de deux fichiers. Avant de pouvoir participer, vous devez cependant sortinger sur la deuxième colonne.

Effectuez des jointures successives dans une boucle for, car join ne prend que deux fichiers comme arguments.

ADDENDA

Voici une petite transcription montrant les jointures successives. Vous pouvez l’adapter assez facilement, je pense.

 $ cat 1.csv a,b,c,d e,f,g,h i,j,k,l $ cat 2.csv 7,5,4,3 3,b,s,e 2,f,5,5 $ cat 3.csv 4,5,6,7 0,0,0,0 1,b,4,4 $ join -t ',' -1 2 -2 2 1.csv 2.csv | cut -f 1 -d ',' > temp $ cat temp b f $ join -t ',' -2 2 temp 3.csv | cut -f 1 -d ',' b 

La première jointure (sur les deux premiers fichiers) produit la valeur jointe dans la première colonne du résultat. Ainsi, lorsque vous vous connectez à file3, file4, file5, etc., vous utiliserez la première colonne du résultat que vous générez, ce qui explique pourquoi vous n’avez besoin que de l’option -2. Pour garder les choses très efficaces, coupez toujours toutes les colonnes sauf la première chaque fois que vous effectuez la jointure.