IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Shell et commandes GNU Discussion :

[bash] Comparer deux fichiers csv


Sujet :

Shell et commandes GNU

  1. #1
    Invité
    Invité(e)
    Par défaut [bash] Comparer deux fichiers csv
    Bonjour à tous,

    Comme indiqué dans le titre, j'essaye de comparer deux fichiers CSV, malheureusement, sur un des fichiers, il y a plusieurs colonnes et je ne veux comparer qu'une des colonnes. Pour être plus claire, voici les fichiers:

    mac_port.csv
    00.1F.FF.5H.66 ; 23 (le ";" étant le délimiteur)
    00.1F.FF.4J.1B ; 14
    port.csv
    23
    je voudrais comparer la deuxième colonne du premier fichier avec le deuxième fichier, et dès qu'il y a concordance, je récupère en sortie la première colonne. Malheureusement, j'y étais arrivé en PowerShell, et Bash ne me simplifie pas la tâche. J'ai tenté avec cmp, diff, mais rien qui ne me permet de faire cela.

    Any ideas ?

  2. #2
    Modérateur
    Avatar de N_BaH
    Profil pro
    Inscrit en
    Février 2008
    Messages
    7 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 7 552
    Points : 19 392
    Points
    19 392
    Par défaut
    Bonjour,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    awk -F' ; ' 'FNR=NR{a[$2]=$1;};{if($1 in a)print a[$1]}' mac_port port
    00.1F.FF.5H.66
    .
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  3. #3
    Rédacteur

    Avatar de ok.Idriss
    Homme Profil pro
    IS Consultant
    Inscrit en
    Février 2009
    Messages
    5 220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : IS Consultant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2009
    Messages : 5 220
    Points : 19 452
    Points
    19 452
    Par défaut
    Bonjour.

    Possible aussi avec join :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [idriss@hp-dv6:~]$ cat f1.txt 
    23 ; dsdjksjfk
    32 ; sdosf
    56 ; sdsldk
    23 ; sfsofksofo
    [idriss@hp-dv6:~]$ cat f2.txt 
    00.1F.FF.5H.66 ; 23
    00.1F.FF.4J.1B ; 14 
    [idriss@hp-dv6:~]$ join -t';' -11 -22 <(sed 's/\ //g' f1.txt|sort) <(sed 's/\ //g' f2.txt|sort)|cut -d ';' -f3|sort -u
    00.1F.FF.5H.66
    Tu peux aussi bricoler avec grep :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    [idriss@hp-dv6:~]$ grep -f <(sed 's/\ //g' f1.txt|sort|cut -d';' -f1) <(sed 's/\ //g' f2.txt|sort)|cut -d ';' -f1
    00.1F.FF.5H.66
    Mais bon, la solution de N_Bah reste plus jolie

    Idriss

  4. #4
    Modérateur
    Avatar de N_BaH
    Profil pro
    Inscrit en
    Février 2008
    Messages
    7 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 7 552
    Points : 19 392
    Points
    19 392
    Par défaut
    j'allais proposer une solution avec join sur la base des fichiers fournis par El_Cypriano
    Code BASH : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ join -t';' -o'1.1' -1 2 -2 1 <(sort -t';' -k2 <(sed 's/ *//g' mac_port)) port
    00.1F.FF.5H.66
    .
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  5. #5
    Invité
    Invité(e)
    Par défaut
    Merci N_BaH et ok.Idriss,

    Les solutions sont bonnes, mais quel est celui qui bouffe un peu de process, parce que ce n'est que le début d'un script long... trop long. Et donc, on m'a dit que "awk" était plus gourmand qu'autre chose, donc join ou grep permettrait d'être plus réactif !?

    Est-ce que vous être d'accord sur cela ?

  6. #6
    Expert éminent

    Profil pro
    Inscrit en
    Janvier 2011
    Messages
    1 946
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 1 946
    Points : 6 276
    Points
    6 276
    Par défaut
    Salut,

    Citation Envoyé par N_BaH Voir le message
    j'allais proposer une solution avec join sur la base des fichiers fournis par El_Cypriano
    Code BASH : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ join -t';' -o'1.1' -1 2 -2 1 <(sort -t';' -k2 <(sed 's/ *//g' mac_port)) port
    00.1F.FF.5H.66
    Et si on considère qu'il y a toujours des espaces avant et après le délimiteur qui est le point virgule, et donc qu'on prenne le délimiteur par défaut (l'espace), on peut faire ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ join  -1 3 -2 1 -o 1.1 f1 f2
    00.1F.FF.5H.66
    $ man woman
    Il n'y a pas de page de manuel pour woman.

  7. #7
    Modérateur
    Avatar de N_BaH
    Profil pro
    Inscrit en
    Février 2008
    Messages
    7 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 7 552
    Points : 19 392
    Points
    19 392
    Par défaut
    vu qu'il faudra, en plus du join, trier au moins un fichier, plus peut-être un ou deux sed (ça dépend du format des fichiers), je pense que awk sera moins gourmand en ressources; d'autant que peut-être tout le script pourrait être écrit en awk...
    .
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  8. #8
    Rédacteur

    Avatar de ok.Idriss
    Homme Profil pro
    IS Consultant
    Inscrit en
    Février 2009
    Messages
    5 220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : IS Consultant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2009
    Messages : 5 220
    Points : 19 452
    Points
    19 452
    Par défaut
    Bonsoir.

    Suffit de faire des tests de perf

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    [idriss@hp-dv6:~]$ content=""; for i in {1..1000}; do content=$(cat f1.txt <(echo -e "$content")); done;
    [idriss@hp-dv6:~]$ content2=""; for i in {1..1000}; do content2=$(cat f2.txt <(echo -e "$content2")); done;
    Pour ma version de join :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    [idriss@hp-dv6:~]$ date +%H:%M:%S:%N;join -t';' -11 -22 <(echo -e"$content"|sed 's/\ //g'|sort) <(echo -e "$content2"|sed 's/\ //g'|sort)|cut -d ';' -f3|sort -u;date +%H:%M:%S:%N
    17:46:56:957289092
    00.1F.FF.5H.66
    17:46:58:287069050
    Pour ma solution avec grep :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    [idriss@hp-dv6:~]$ date +%H:%M:%S:%N;grep -f <(echo -e "$content"|sed 's/\ //g'|sort|cut -d';' -f1) <(echo -e "$content2"|sed 's/\ //g'|sort)|cut -d ';' -f1|sort -u;date +%H:%M:%S:%N
    17:49:11:036894800
    00.1F.FF.5H.66
    17:49:11:148334877
    C'est rapide grep, en fait

    Et une solution avec awk inspirée de la solution de N_Bah mais permettant d'éviter les doublons sans passer par sort :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk -F' ; ' 'FNR=NR{a[$2]=$1;};{if($1 in a)t[a[$1]]=a[$1]} END {for (i in t)print t[i]}' f2.txt f1.txt
    Voici pour le test de perf :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    [idriss@hp-dv6:~]$ date +%H:%M:%S:%N;awk -F' ; ' 'FNR=NR{a[$2]=$1;};{if($1 in a)t[a[$1]]=a[$1]} END {for (i in t)print t[i]}' <(echo -e "$content2") <(echo -e "$content");date +%H:%M:%S:%N
    17:55:11:264311606
    00.1F.FF.5H.66
    17:55:11:289953146
    => Quasi instantané également.

    Pour départager grep et awk, on pourrait faire un test de perf à plus grande échelle (10 000 fois le contenu par exemple) mais bon le CPU de mon petit laptop morfle trop

    Comme dit N_Bah, la solution awk à l'avantage de n'utiliser qu'un processus donc préférable et est probablement plus performante à plus grande échelle.

    Idriss

  9. #9
    Expert éminent sénior Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 243
    Points : 13 459
    Points
    13 459
    Par défaut
    Comme chacun y va de sa solution, j'y vais aussi:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    $ cat initial.txt 
    00.1F.FF.5H.66 ; 23
    00.1F.FF.4J.1B ; 14 
    $ cat filtre.txt 
    23
    $ sed 's/.*/;.*&/' filtre.txt|grep -f - initial.txt |sed 's/;.*$//'
    00.1F.FF.5H.66 
    $
    Cette réponse vous apporte quelque chose ? Cliquez sur en bas à droite du message.

  10. #10
    Expert éminent sénior Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 286
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 286
    Points : 12 743
    Points
    12 743
    Par défaut
    Sur le même model que Flodelarab () mais en virant le grep:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    $ cat initial.txt
    00.1F.FF.5H.66 ; 23
    00.1F.FF.4J.1B ; 14
    $ cat filtre.txt
    23
    $ sed 's/.*/s\/;.*&\/\/p/' filtre.txt|sed -n -f - initial.txt
    00.1F.FF.5H.66
    Cordialement.

  11. #11
    Rédacteur

    Avatar de ok.Idriss
    Homme Profil pro
    IS Consultant
    Inscrit en
    Février 2009
    Messages
    5 220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : IS Consultant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2009
    Messages : 5 220
    Points : 19 452
    Points
    19 452
    Par défaut
    Si on considère que le second fichier n'est pas un vrai csv et ne contient que les valeurs correspondant à la seconde et que les valeurs de la première colonne sont unique, ceci suffirait

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    grep -f f2.txt f1.txt | awk -F " ; " '{print $1}'

  12. #12
    Expert éminent sénior Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 243
    Points : 13 459
    Points
    13 459
    Par défaut
    Sur le même model que Flodelarab () mais en virant le grep:
    Code :

    $ cat initial.txt
    00.1F.FF.5H.66 ; 23
    00.1F.FF.4J.1B ; 14
    $ cat filtre.txt
    23
    $ sed 's/.*/s\/;.*&\/\/p/' filtre.txt|sed -n -f - initial.txt
    00.1F.FF.5H.66
    Joli! J'en remets une couche: (moins de "\" )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sed 's@.*@s/;.*&//;t bon;d;:bon;p@' filtre.txt|sed -n -f - initial.txt
    Cette réponse vous apporte quelque chose ? Cliquez sur en bas à droite du message.

  13. #13
    Expert éminent sénior Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 243
    Points : 13 459
    Points
    13 459
    Par défaut
    Citation Envoyé par ok.Idriss Voir le message
    Si on considère que le second fichier n'est pas un vrai csv et ne contient que les valeurs correspondant à la seconde et que les valeurs de la première colonne sont unique, ceci suffirait

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    grep -f f2.txt f1.txt | awk -F " ; " '{print $1}'
    Euh sauf si le premier champ contient 23
    Cette réponse vous apporte quelque chose ? Cliquez sur en bas à droite du message.

  14. #14
    Rédacteur

    Avatar de ok.Idriss
    Homme Profil pro
    IS Consultant
    Inscrit en
    Février 2009
    Messages
    5 220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : IS Consultant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2009
    Messages : 5 220
    Points : 19 452
    Points
    19 452
    Par défaut
    Pas faux

    Bien vu

  15. #15
    Expert éminent sénior Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 286
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 286
    Points : 12 743
    Points
    12 743
    Par défaut
    Citation Envoyé par Flodelarab Voir le message
    Joli! J'en remets une couche: (moins de "\" )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sed 's@.*@s/;.*&//;t bon;d;:bon;p@' filtre.txt|sed -n -f - initial.txt
    Une surcouche
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    xargs -a filtre.txt -ITT sed -n "s/;.*TT//p" initial.txt
    Cordialement.

  16. #16
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    792
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Mai 2004
    Messages : 792
    Points : 1 206
    Points
    1 206
    Par défaut
    Citation Envoyé par N_BaH Voir le message
    Bonjour,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    awk -F' ; ' 'FNR=NR{a[$2]=$1;};{if($1 in a)print a[$1]}' mac_port port
    00.1F.FF.5H.66
    Je dirais plutôt:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk -F' ; ' 'FNR==NR{a[$2]=$1;next}a[$1]{print a[$1]}' mac_port port
    FNR=NR est toujours vrai. Il manque aussi, me semble-t-il, l'instruction next non?
    :q :q! :wq :w :w! :wq! :quit :quit! :help help helpquit quit quithelp
    :quitplease :quitnow :leave :shit ^X^C ^C ^D ^Z ^Q QUITDAMMIT
    Jabber: ripat at im.apinc.org

  17. #17
    Expert éminent sénior Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 243
    Points : 13 459
    Points
    13 459
    Par défaut
    Non. FNR et NR ne sont égaux que pour le premier fichier en entrée. Pour le second, NR retombe à 0 et FNR poursuit sa route.

    C'est pour cela que next est inutile et que la seconde accolade n'a pas besoin de condition. En effet, l'instruction "for i in ..." s'applique aux indices et pas aux valeurs. Aucun indice du tableau n'est un port puisque ce sont les adresses.

    Par contre, j'aurais mis "FNR==NR" et pas "="
    Cette réponse vous apporte quelque chose ? Cliquez sur en bas à droite du message.

  18. #18
    Expert éminent sénior Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 286
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 286
    Points : 12 743
    Points
    12 743
    Par défaut
    Citation Envoyé par El_Cypriano Voir le message
    Merci N_BaH et ok.Idriss,

    Les solutions sont bonnes, mais quel est celui qui bouffe un peu de process, parce que ce n'est que le début d'un script long... trop long. Et donc, on m'a dit que "awk" était plus gourmand qu'autre chose, donc join ou grep permettrait d'être plus réactif !?

    Est-ce que vous être d'accord sur cela ?
    Cela peut dépendre beaucoup de ton contexte, comme par exemple, quel sera la quantité de valeur dans ton 2éme fichier, juste une ???
    Ainsi que la quantité du 1er fichier ?
    Le but est de savoir si on doit s'arranger pour traiter tes fichiers en une seule passe (ici c'est le cas pour toutes les méthodes présentées car on a q'une seule donner à traiter dans le 2éme fichier sinon, plusieurs solutions ne sont pas bonnes du tout comme par exemple celle que j'ai proposée avec le xargs et le sed) et le awk peut être avantageux dans ce type de cas.
    Cordialement.

  19. #19
    Modérateur
    Avatar de N_BaH
    Profil pro
    Inscrit en
    Février 2008
    Messages
    7 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 7 552
    Points : 19 392
    Points
    19 392
    Par défaut
    Non. FNR et NR ne sont égaux que pour le premier fichier en entrée.
    le problème n'est pas là, mais dans mon oubli d'un second =
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    = #est une assignation, et donc toujours vrai
    == #est un opérateur de test
    .
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  20. #20
    Expert éminent sénior Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 243
    Points : 13 459
    Points
    13 459
    Par défaut
    Le tout est de se comprendre. Nous sommes tous les 3 d'accord.


    Les solutions sont bonnes, mais quel est celui qui bouffe un peu de process, parce que ce n'est que le début d'un script long... trop long. Et donc, on m'a dit que "awk" était plus gourmand qu'autre chose, donc join ou grep permettrait d'être plus réactif !?

    Est-ce que vous être d'accord sur cela ?
    Une expérience perso:
    • fonctions natives de awk: Echec (100x trop long)
    • grep:
      real 0m43.304s
      user 0m24.802s
      sys 0m14.385s
    • grep -q
      real 0m8.246s
      user 0m2.262s
      sys 0m4.277s
    Donc, ma conclusion a été franchement contre awk. Son point fort est le traitement par champ. Sinon, je prends autre chose.
    Il faut dire que je traitais un gros dictionnaire.
    Cette réponse vous apporte quelque chose ? Cliquez sur en bas à droite du message.

Discussions similaires

  1. [CSV] Comparer deux fichiers CSV
    Par Ghirahim dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 10
    Dernier message: 03/06/2015, 16h23
  2. Comparer deux fichiers csv
    Par kerplouz dans le forum Général Python
    Réponses: 1
    Dernier message: 21/11/2012, 21h17
  3. Comparer deux fichier
    Par Taz_8626 dans le forum Langage
    Réponses: 3
    Dernier message: 20/06/2006, 11h46
  4. comparer deux fichiers avec une api windows
    Par sweetdreamer dans le forum Windows
    Réponses: 4
    Dernier message: 25/05/2006, 22h10
  5. Fonction c qui compare deux fichiers ???
    Par babyface dans le forum C
    Réponses: 4
    Dernier message: 19/11/2005, 13h07

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo