Forum Programmation.shell Comparer 2 fichiers plat avec awk

Posté par  .
Étiquettes : aucune
0
2
août
2010
Bonjour tout le monde,

J'aurai besoin d'un coup de main. J'ai 2 fichiers plat txt qui ressemble un peu à ca :

Contenu du fichier 1 :

a
b
c
d
e
f
g

Contenu du fichier 2 :

a
f

Comment je peux faire avec awk pour afficher sur la sortie standard le contenu du fichier 1 sans les lignes du fichier 2 ? A savoir...

b
c
d
e
g

Merci par avance pour le coup de main !
  • # Parcequ'il faut un sujet.

    Posté par  . Évalué à 2.

    awk 'BEGIN { do { exit_status=getline tmp < "fichier2"; lignes_a_supprimer[tmp]=1} while(exit_status > 0);} {if (!($0 in lignes_a_supprimer)) {print}}' < fichier1
    • [^] # Re: Parcequ'il faut un sujet.

      Posté par  (site web personnel) . Évalué à 3.

      quand je pense que awk est d'une simplicité enfantine, je suis toujours surpris de voir que la plupart des gens se sentent obligé de le faire en mode oneliner imbitable. Vous savez qu'on peut aussi le mettre dans un fichier bien propre et faire awk -f monfichier.awk ? :D

      Sinon, en python ça donne :

      with open("txt1.txt") as f1:
          lines1 = f1.readlines()
          with open("txt2.txt") as f2:
              lines2 = f2.readlines()
              lines = [line.strip() for line in lines1 if line not in lines2]

      for line in lines:
          print line


      Je sais c'était pas la question, mais ça me fait plaisir :)
      • [^] # Re: Parcequ'il faut un sujet.

        Posté par  . Évalué à 1.

        @cho7 : J'utilise awk. Mais j'avoue que j'ai encore du mal à comprendre toute ses ficelles. C'est pas faute d'essayer ! :-s

        Merci pour vos remarques, je vais tester ça demain et je repasse par ici pour voir si ça répond bien à mon besoin.
        • [^] # Re: Parcequ'il faut un sujet.

          Posté par  (site web personnel) . Évalué à 2.

          Pour comprendre awk, apprend le en mode script, pas en mode oneliner comme le monsieur du dessus te l'a montré.

          awk c'est vraiment simple, je te redirige vers ce tutorial qui je suis sûr va éclairer tes lanternes : http://www.grymoire.com/Unix/Awk.html

          Après pour ta problématique, il y a plein d'autres trucs mieux que awk, notamment la solution grep évoquée plus bas par Krunch, même si j'émets des réserves dans la mesure où ton fichier 2 ne doit alors contenir aucun caractères joker sinon le résultat pourrait être différent de celui attendu voir ca peut générer une erreur si une ligne correspond à un mauvais format de pattern.

          Krunch tu confirmes ?
          • [^] # Re: Parcequ'il faut un sujet.

            Posté par  (site web personnel) . Évalué à 2.

            bon a priori avec -F on doit pouvoir dire que le fichier spécifié par -f ne contient pas de regexp mais uniquement des chaines de caractères normales.
          • [^] # Re: Parcequ'il faut un sujet.

            Posté par  . Évalué à 1.

            Apprendre en mode script ? C'est vrai que c'est plus simple qu'en onliner du point de vu de la syntaxe. Je fais du onliner uniquement pour des cas très simple de filtre de colonne en fait ou pour de la substition de chaine généralement.
            Pour des cas plus "compliqués" (pour moi) c'est vrai que awk en mode online, ce n'est pas gérable.

            Merci en tout cas pour vos retours.
  • # grep -v -f fichier2 fichier1

    Posté par  (site web personnel) . Évalué à 8.

    Parce qu'il faut un commentaire...

    pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

    • [^] # Re: grep -v -f fichier2 fichier1

      Posté par  (site web personnel) . Évalué à 1.

      hihi, c'était mon étape 2, j'étais sûr qu'on pouvait faire ça à coup de grep il fallait juste que je découvre l'existence du -f :)

      Question bonus que l'auteur de ce topic ne posera surement pas mais qui m'intéresse, si dans le fichier spécifié par -f il y a des caractères joker, il y a moyen de les interpréter comme des caractères normaux ?
      • [^] # Re: grep -v -f fichier2 fichier1

        Posté par  (site web personnel) . Évalué à 1.

        > si dans le fichier spécifié par -f il y a des caractères joker,
        > il y a moyen de les interpréter comme des caractères normaux ?

        Oui.

        pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

      • [^] # Re: grep -v -f fichier2 fichier1

        Posté par  . Évalué à 2.

        Rajouter -F et être sûr de ne pas avoir de ligne vide dans fichier2 (une ligne vide "match" tout).
        Mais par contre, on ne peut plus virer les lignes vides de fichier1. Faut repasser un coup de grep derrière.
        • [^] # Re: grep -v -f fichier2 fichier1

          Posté par  . Évalué à 3.

          Et puis rajouter -x aussi, parce qu'une ligne de fichier2 peut être inclue dans une ligne de fichier1 sans être égale, c'est à dire que la ligne de fichier2 peut n'être qu'une sous-chaîne de la ligne de fichier1, pas la ligne entière.

          Et puis, tant que j'y suis, une technique que j'ai lue sur un blog :
          cat fichier1 fichier2 fichier2 | sort | uniq -u

          Mais le résultat est dans le désordre par rapport à fichier1 et il faut que toutes les lignes de fichier1 soient uniques.
          Et c'est aussi peut être plus long.
          • [^] # UUOC

            Posté par  (site web personnel) . Évalué à 3.

            Je pense que dans le pire cas, sort|uniq est plus performant que grep -xvFf. Si on pose f1 le nombre de lignes dans fichier1 et f2 le nombre de lignes dans fichier2, je pense qu'on a O(f1)*O(f2) pour grep contre O((f1+f2)*log(f1+f2)) pour sort|uniq.

            Dans le cas général, grep reste sans doute plus rapide.

            Sinon je note qu'il manque une page Wikipedia sur Commentz-Walter.

            pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

  • # diff

    Posté par  (site web personnel) . Évalué à 5.

    Comparer des fichiers, ça ne fait pas penser à diff ?

    La solution la plus naturelle :

    diff a b | grep '^>' | cut -b 3-

    Ou en jouant avec sed :

    diff a b | sed -n 's/> \(.*\)$/\1/gp'
    • [^] # Re: diff

      Posté par  . Évalué à 2.

      Evidemment, ça, ça marche bien, et ça fait ce que je demande. Mais pour ma culture, j'aurai bien aimé l'avoir en awk. Mais j'ai eut des retours plus haut sur les commentaires.

      Merci à toi en tout cas :-)
  • # Join

    Posté par  . Évalué à 5.

    Ce n'est pas pour troller, mais je pense que la solution la plus immediate est d'utiliser join, qui est explicitement fait pour ce cas de figure :-)

    join -v 1 file1 file2
    • [^] # Re: Join

      Posté par  . Évalué à 2.

      Et j'en profites pour répondre à la question :

      awk 'NR == FNR{ligne[$1]=1} NR != FNR {if(!($1 in ligne))print}' file2 file1

      ou en formaté correctement ;-)

      NR == FNR { ligne[$1] = 1 }
      NR != FNR {
      if ( !($1 in ligne))
      print
      }

      avec en entrée file2 file1

      Explications :

      l'astuce FNR == NR sert à savoir si on est en train de lire le premier fichier ou le second.
      Donc si on est en train de lire file2, FNR == NR, et on remplit un tableau avec pour clef l'entrée qui se trouve sur la ligne.
      Si on lit le second fichier (qui est en fait file1), on n'a plus qu'à verifier que la ligne lue n'était pas présente dans le fichier 2 (l'operateur "in" sur le tableau "ligne"). Si c'est le cas, on affiche la ligne.
      • [^] # Re: Join

        Posté par  . Évalué à 1.

        Si on commence à me filer les syntaxe awk en mode onliner et mode script : là c'est ce que j'appelle de la réponse **GOLD** :-)

        Merci !
  • #

    Posté par  . Évalué à 5.

    man comm

    ... Parce qu'il ne faut pas de titre.
    • [^] # Re:

      Posté par  . Évalué à 1.

      Sacré Totof2000, toujours à sortir des commandes de son chapeau... Et en awk alors ?
      • [^] # Re: Re:

        Posté par  . Évalué à 2.

        En awk ? pas possible, me suis fait mal au dos en voulant ramasser un gros Ruby.
    • [^] # Re:

      Posté par  (site web personnel) . Évalué à 2.

      Ouaip. La commande idéale.

      comm -23 un.txt deux.txt
      • [^] # Re: Re:

        Posté par  . Évalué à 2.

        on peut juste lui reprocher de :
        - ne pas être POSIX
        - avoir un format d'ouput ...discutable
        - utiliser des paramètres arbitraires (1, 2, 3 ... soleil ?). Enfin ça, c'est une conséquence du point précédent.

        sinon, je ne la connaissais pas non plus. C'est toujours bien pour la culture, mais je vais surtout m'empresser de jamais m'en servir :-)
        • [^] # Re: Re:

          Posté par  (site web personnel) . Évalué à 2.

          - man comm

          The comm utility conforms to IEEE Std 1003.2-1992 (``POSIX.2'').
          The -i option is an extension to the POSIX standard.


          - Il me sort bien un mot par ligne, qui est dans un.txt mais pas dans deux.txt, comme attendu.

          - entièrement d'accord.


          Je fais le blasé, mais j'ai découvert la commande deux jours avant qu'on poste ce journal, en ayant besoin de calculer l'intersection et la différence d'ensembles en bash (pour un script d'install qui recommence en cas d'erreur sans repartir à zéro).
          • [^] # Re: Re:

            Posté par  . Évalué à 2.

            ah oui, tiens, c'est POSIX, mais pas précisé dans le man/info comm sur debian.

            concernant l'output, je ne lui reproche pas de ne pas fonctionner hein. C'est juste que je suis pas fan du csv :D

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.