(sed) extraction d'une partie d'un fichier if

oqp24 Messages postés 10 Statut Membre -  
oqp24 Messages postés 10 Statut Membre -
Extraction de chaine avec sed
Sur Unix, j’ai un fichier composé de plusieurs paragraphes.
Tous les paragraphes commence par DDD et finissent par FFF.
Certain paragraphes contiennent la chaine XXX.

Exemple
DDDblablabla
blablablalll
blablablammmmmmmm
FFF blablablass
DDD blablabla
blablablaoo
lsdqkgsqdXXX blablabla
FFF blablabladd
DDD blablablaqq
blablablasd
FFF blablabla

1 : Comment extraire les paragraphes contenant la chaine XXX.(Parag 2)

2 : Comment extraire les paragraphes ne contenant pas la chaine XXX. (Parag 1 et 3)

sed -en "/^DDD/ , /^FFF/p" -en "/XXX/w resultat" file
extrait tout le fichier moins la ligne contenant XXX

J’ai tourné le sed et le awk dans tous les sens mais je n’avance plus.
Merci

9 réponses

  1. lami20j Messages postés 21506 Date d'inscription   Statut Modérateur, Contributeur sécurité Dernière intervention   3 571
     
    Salut,

    en fait on n'a même pas besoin d'avoir des paragrphes tant qu'on est sur de ça

    commence par DDD et finissent par FFF.

    #! /usr/bin/perl
    
    use warnings;use strict;
    undef $/;
    my $fic = <>;
    pos($fic)=0;
    
    while ( $fic =~ /DDD/g ){
       if ($fic =~ /\G(.*XXX.*FFF)/){
        print $1;
      }
    }
    Je ne l'ai pas testé mais l'algo est suivant :

    - je récupère le contenu de fichier dans une variable scalaire (mode slurp)
    - je fais une recherche progressive de DDD et j'affiche le contenu de la chaine jusqu'à FFF uniquement si quelque part j'ai XXX entre DDD et FFF)

    pour ça j'utilise le modificateur g et l'assertion \G

    Si je me rappelle bien Dal a des bonnes connaissances en Perl donc il peut corriger mon script. Je suis sous Win maintenant et je ne peux pas tester.

    Pour execution

    perl /rep/scripts/extrat.pl nom_fichier

    lami20j
    1
    1. oqp24 Messages postés 10 Statut Membre
       
      C'est sympa mais je ne dois utiliser que du shell unix, pas de Perl.
      Je suis sur une tres grosse prod et il y a donc bcp de restriction.

      Dommage
      0
      1. lami20j Messages postés 21506 Date d'inscription   Statut Modérateur, Contributeur sécurité Dernière intervention   3 571 > oqp24 Messages postés 10 Statut Membre
         
        Re,

        d'accord mais Perl est integré dans les système Unix donc normalement tu ne dois pas avoir des problèmes.

        En fait les seul problèmes que tu peux avoir c'est la version de Perl, puisque les regex de Perl on bien evoulées.

        lami20j
        0
      2. oqp24 Messages postés 10 Statut Membre > oqp24 Messages postés 10 Statut Membre
         
        En fait cela fait partie d'un gros projet et donc le shell unix est imposé...et en + je ne connais pas Perl...
        0
  2. asevere Messages postés 13095 Date d'inscription   Statut Webmaster Dernière intervention   426
     
    Bonsoir,

    Une astuce pour ne pas trop te pourrir la vie :)

    Avec sed seulement, on oublie (travail ligne/ligne, donc pas top)
    Avec awk seulement, ça doit être fesable, mais imbittable

    Avec les deux ensemble par contre :)

    awk travaillant sur des enregistrements et non sur des lignes ( le separateur d'enregistrement est \n par defaut) ça doit être réalisable....

    Voyons un peu...

    La premiere chose a faire est d'avoir un separateur de champ...
    sed -e 's/\(^DDD.*\)/RECORD_SEP\1/' -e 's/\(^FFF.*\)/\1RECORD_SEP/' fichier
    Là on ajoute RECORD_SEP avant les lignes commencant par DDD et aprés les lignes commencant par FFF.

    On obtient quelque chose du genre:
    RECORD_SEPDDDblablabla
    blablablalll
    blablablammmmmmmm
    FFF blablablassRECORD_SEP
    RECORD_SEPDDD blablabla
    blablablaoo
    lsdqkgsqdXXX blablabla
    FFF blablabladdRECORD_SEP
    RECORD_SEPDDD blablablaqq
    blablablasd
    FFF blablabla RECORD_SEP
    Soit 6 enregistrements (C'est la faiblesse), en affinant, on doit pouvoir recuperer 3 enregistrements
    IE:
    DDDblablabla
    blablablalll
    blablablammmmmmmm
    FFF blablablass
    RECORD_SEPDDD blablabla
    blablablaoo
    lsdqkgsqdXXX blablabla
    FFF blablabladd
    RECORD_SEPDDD blablablaqq
    blablablasd
    FFF blablabla
    Mais c'est pas grave :)
    Maintenant, qu'on a ces champ, il suffit de passer ça a awk, en lui demendant d'afficher les enregistrement qui nous sont utiles...

    Quelques infos:
    Pour ecraser le separateur de champ par defaut, il faut renseigner la variable RS dans un bloque BEGIN tant qu'a faire pour ne le faire qu'une fois au début du traitement
    BEGIN{ RS="RECORD_SEP" }
    la syntaxe pour afficher un enregistrement s'il correspond a une reg_exp est:
    /reg_exp/{ print $0} ($0 représente l'enregistrement courant)
    L'inverse:
    !/reg_exp/{ print $0}
    Ce qui nous donne:
    awk 'BEGIN{ RS="RECORD_SEP" } /DDD.*XXX.*FFF.*/ { print $0 }'
    pour matcher un paragraphe avec XXX et
    awk 'BEGIN{ RS="RECORD_SEP" } !/DDD.*XXX.*FFF.*/ && /DDD.*FFF.*/ { print $0 }'
    pour matcher un paragraphe sans XXX

    Pour info:
    L'expression !/DDD.*XXX.*FFF.*/ ne suffit pas, elle retourne aussi les enregistrement vide (dus aux RECORD_SEP RECORD_SEP généré par sed plus haut)

    Donc:
    $ sed -e's/\(^DDD.*\)/RECORD_SEP\1/' -e 's/\(^FFF.*\)/\1RECORD_SEP/' test |awk 'BEGIN{ RS="RECORD_SEP" } /DDD.*XXX.*FFF.*/ { print $0 }'
    DDD blablabla
    blablablaoo
    lsdqkgsqdXXX blablabla
    FFF blablabladd
    $ sed -e's/\(^DDD.*\)/RECORD_SEP\1/' -e 's/\(^FFF.*\)/\1RECORD_SEP/' test |awk 'BEGIN{ RS="RECORD_SEP" } !/DDD.*XXX.*FFF.*/ && /DDD.*FFF.*/ { print $0 }'
    DDDblablabla
    blablablalll
    blablablammmmmmmm
    FFF blablablass
    DDD blablablaqq
    blablablasd
    FFF blablabla
    Ca doit repondre à ton attente :)

    (PS: je sais, c'est optimisable)
    1
    1. lami20j Messages postés 21506 Date d'inscription   Statut Modérateur, Contributeur sécurité Dernière intervention   3 571
       
      Salut,

      ça fait un bail. Commen tu vas?

      Je crois que jipicy n'aura plus des maux de têtes.

      Je suis trop paresseux pour faire un truc pareil. Félicitations!

      lami20j
      0
      1. asevere Messages postés 13095 Date d'inscription   Statut Webmaster Dernière intervention   426 > lami20j Messages postés 21506 Date d'inscription   Statut Modérateur, Contributeur sécurité Dernière intervention  
         
        Ava bien,

        Beaucoup de boulot, alors je post moins :)
        0
  3. [Dal] Messages postés 6122 Date d'inscription   Statut Contributeur Dernière intervention   1 108
     
    Salut,

    Je ne vois pas très bien où tu places tes "paragraphes" 1, 2 et 3 dans ton exemple ci-dessus.

    Si ce que tu veux afficher ce sont des lignes (c'est à dire une suite de caractères avec CR au bout), tu peux utiliser grep, et l'option -v ou --invert-match pour les lignes ne contenant pas l'objet de la recherche (du moins dans le GNU grep).

    Dal
    0
    1. oqp24 Messages postés 10 Statut Membre
       
      Non ce serai trop simple.

      Comment extraire les paragraphes contenant
      la chaine XXX .(Parag 2)
      on doit obtenir:

      DDD blablabla
      blablablaoo
      lsdqkgsqdXXX blablabla
      FFF blablabladd

      Comment extraire les paragraphes ne contenant pas
      la chaine XXX. (Parag 1 et 3)
      on doit obtenir:

      DDDblablabla
      blablablalll
      blablablammmmmmmm
      FFF blablablass
      DDD blablablaqq
      blablablasd
      FFF blablabla
      0
  4. lami20j Messages postés 21506 Date d'inscription   Statut Modérateur, Contributeur sécurité Dernière intervention   3 571
     
    Encore une chose si jamais tu essaies le script.

    Il faut inverser le sens puisque tu as besoin de Comment extraire les paragraphes ne contenant pas

    Donc au lieu de

    $fic =~ /\G(.*XXX.*FFF)/

    écrit

    $fic !~ /\G(.*XXX.*FFF)/

    lami20j
    0
  5. Vous n’avez pas trouvé la réponse que vous recherchez ?

    Posez votre question
  6. oqp24 Messages postés 10 Statut Membre
     
    Argh stop pitié pas de PERL
    Vos solution sont surement bonne mais hélas ne me servent pas.
    0
    1. lami20j Messages postés 21506 Date d'inscription   Statut Modérateur, Contributeur sécurité Dernière intervention   3 571
       
      stop pitié pas de PERL ok, je ne dit plus rien.

      lami20j
      0
      1. oqp24 Messages postés 10 Statut Membre > lami20j Messages postés 21506 Date d'inscription   Statut Modérateur, Contributeur sécurité Dernière intervention  
         
        Merci quand meme
        Have a good time
        0
  7. [Dal] Messages postés 6122 Date d'inscription   Statut Contributeur Dernière intervention   1 108
     
    Re,

    C'est dommage de ne pas utiliser Perl, car c'est un outil très puissant pour la manipulation de texte.

    Je comprend mieux ton concept de "paragraphe".

    Je ne connais pas bien awk, mais s'agissant de sed, à ma connaissance celui-ci travaille sur des lignes. Là tes "paragraphes" sont composés d'un nombre indéterminé de lignes avec des délimiteurs situés sur des lignes différentes.

    Tu peux cependant faire un script shell qui met dans une variable les "paragraphes" en question en fonction des délimiteurs et qui contrôle pour chaque ligne faisant partie du paragraphe si ton marqueur "XXX" y figure.

    Tu fais une boucle (par exemple en bash ou sh) :

    while read ligne
    do

    (...)

    done <les_datas.txt

    et dedans tu mets tes tests sed sur chaque $ligne, la variable de stockage des paragraphes (initialisée si c'est un marqueur de début, mise à jour sinon), un drapeau si le marqueur "XXX" est présent, un test d'affichage du contenu de la variable de stockage du paragraphe courant en fonction du drapeau (si le marqueur de fin de paragraphe est atteint), etc.

    Je n'ai pas le temps de te faire le script tout cuit, mais c'est comme çà que je ferai.

    Dal
    0
  8. oqp24 Messages postés 10 Statut Membre
     
    N'ayant pas le choix, j'ai deja commencé un truc dans ce genre.
    Dommage de ne pas avoir la commande toute simple qui tue, car les fichiers à traiter sont énormes.

    Merci tout de même.. En esperant qu'un petit genie...
    0
  9. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
     
    Salut Adrien,

    Chapeau bas Mister Severe ;-))

    Effectivement comme l'a dit lami20j, tu m'ôtes des maux de la boîte cranienne ;-))

    Bon en attendant en essayant d'optimiser (un tant soit peu) je suis arrivé à ça :
    sed 's/\(DDD\)/==\1/' fich.txt | awk 'BEGIN { RS="==" } !/XXX/ { print $0 }'
    
    DDDblablabla
    blablablalll
    blablablammmmmmmm
    FFF blablablass
    
    DDD blablablaqq
    blablablasd
    FFF blablabla
    
    $ sed 's/\(DDD\)/==\1/' fich.txt | awk 'BEGIN { RS="==" } /XXX/ { print $0 }'
    DDD blablabla
    blablablaoo
    lsdqkgsqdXXX blablabla
    FFF blablabladd
    Merci Adrien.
    0
  10. [Dal] Messages postés 6122 Date d'inscription   Statut Contributeur Dernière intervention   1 108
     
    Bravo !

    Content de te revoir sur le forum :)

    En fait, on peut même se passer de sed, puisque "DDD" est déjà un délimiteur :) .. on pourrai simplement le remettre dans l'instruction print.
    $ awk 'BEGIN { RS="DDD" } /XXX/ { print "DDD"$0 }' < fich.txt
    DDD blablabla
    blablablaoo
    lsdqkgsqdXXX blablabla
    FFF blablabladd
    
    $ awk 'BEGIN { RS="DDD" } \!/XXX/ && /.*FFF.*/ { print "DDD"$0 }' < testsed.txt
    DDDblablabla
    blablablalll
    blablablammmmmmmm
    FFF blablablass
    
    DDD blablablaqq
    blablablasd
    FFF blablabla

    Enfin, pour que celà marche (cette syntaxe, comme celle de jipicy), il faut que les délimiteurs DDD et FFF ne risquent pas de se retrouver ailleurs dans le corps du paragraphe et que l'on n'ait donc pas à vérifier qu'ils soient en début de ligne.

    Dal
    0
    1. lami20j Messages postés 21506 Date d'inscription   Statut Modérateur, Contributeur sécurité Dernière intervention   3 571
       
      Salut,

      on peut même se passer de sed, puisque "DDD" est déjà un délimiteur

      C'est exactement à ça que j'ai pensé, mais t'es plus rapide que moi.

      Awk je ne connais pas du tout, donc hier soir après la solution d'asevere j'ai commencé à lire un peu awk et ce matin en train d'aller au boulot j'ai pense "mais pourquoi on crée un delimiteur s'il existe déjà". Mais comme tu le dit, ça ne marche que si les délimiteurs DDD et FFF

      De toute façon parser du texte ça veut dire parser de texte. Maintenant il faut voir les capacités des outils qu'on utilise.

      Et Perl n'est qu'un fils de sed et awk, qui c'est vrai a bien agrandi depuis....

      lami20j
      0
    2. oqp24 Messages postés 10 Statut Membre
       
      Bravo Severe et merci pour ton aide ca marche nickel.

      Merci aussi aux autres mais helas comme l'a si bien dit DAL, les delimitteurs, pour mon cas peuvent se retrouver n'importe ou dans le fichier.

      A+
      0