Syntaxe nawk

Résolu
unixnewbie -  
 unixnewbie -
Bonjour,

Je dispose d'une commande sympa (récupérée sur CCM) qui permet de faire l'équivalent d'un grep -nombre (lorsque l'option n'est pas dispo dans la distrib). Cette option permet d'avoir le contexte (nombre pour nombre de lignes du contexte) pour un pattern recherché.
Voici la commande en nawk:
nawk 'c-->0;$0~s{if(b)for(c=b+1;c>1;c--)print r[(NR-c+1)%b];print;c=a}b{r[NR%b]=$0}' b=10 a=10 s="pattern" file
équivalent à
grep -10 "pattern" file

Voilà pour l'intro.
De mon côté, ce que je souhaiterai avoir, c'est le paragraphe introduit par une chaîne de caractère particulière et finissant par une autre chaîne de caractère dans lequel il y a le pattern que je cherche.

Exemple:
Je
m'appelle
toto
et
je
suis
gros
Je
m'appelle
titi
et
je
suis
petit


Quelle syntaxe de nawk me donnerait
Je
m'appelle
titi
et
je
suis

si je dis que je recherche "titi" entre "Je" et "suis" ?

Merci de votre aide !

19 réponses

  1. dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   5 660
     
    puisqu'on traite plusieurs fichiers, je ne réinitialisais pas les variables, j'ai ajouté a=b=0 au début du traitement de chaque fichier

    essaye
    nawk 'FNR==1 {f=1;a=b=0}; {x[FNR]=$0}; /<Commande>/ {a=FNR}; /LULU/ {b=FNR}; /<\/Commande>/ {if(b  > a && b < FNR)for(n=a;n<=FNR;){if(f){print FILENAME ":";f=0}print x[n++]}}'  *
    1
  2. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
     
    Salut,

    Il te faut ça spécialement avec nawk ? (je pense à "sed")
    0
  3. lami20j Messages postés 21506 Date d'inscription   Statut Modérateur, Contributeur sécurité Dernière intervention   3 571
     
    Salut,

    (je pense à "sed")

    Fini ta pensée ;-)
    0
  4. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
     
    Ben non, si ce n'est pas la peine (bien que ce soit prêt ;-)) )
    0
  5. Vous n’avez pas trouvé la réponse que vous recherchez ?

    Posez votre question
  6. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
     
    Ok.

    Sur une ligne ça donne :

    sed -n '/Je/{:z;N;/suis/!bz;/titi/p}' toto


    Exemple plus aéré :
    jp@MDK:~/tmpfs ssh$ cat toto
    Je
    m'appelle
    toto
    et
    je
    suis
    gros
    Je
    m'appelle
    titi
    et
    je
    suis
    petit
    
    jp@MDK:~/tmpfs ssh$ sed -n '
    /Je/ {
    :z
    N
    /suis/ !b z
    /titi/ p
    }
    ' toto
    Je
    m'appelle
    titi
    et
    je
    suis
    jp@MDK:~/tmpfs ssh$
    0
  7. unixnewbie
     
    Merci !

    Question bonux: j'ai des slashs dans mes patterns de recherche (c'est du xml). Les escaper ne semble pas marcher. Il faut des guillemets particuliers ?

    exemple:
    sed -n '/<Chaine1>/{:z;N;/<\/Chaine2>/!bz;/Monpattern/p}' toto
    0
    1. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
       
      Normalement ça devrait ;-\

      Essaye en changeant le délimiteur du motif :

      sed -n '/<Chaine1>/{:z;N;\#<~/Chaine2>#!bz;/Monpattern/p}' toto
      0
  8. unixnewbie
     
    En fait, après un test basique, je me rend compte que le pb ne vient pas du backslash mais du label:

    sed -n '/Je/{:z;N;/suis/!bz;/titi/p}' titi
    Label too long: /Je/{:z;N;/suis/!bz;/titi/p}


    Je suis sous Solaris.
    Faut que je migre sous linux ? ;-)
    0
    1. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
       
      C'est pour ça que je t'ai donné aussi la syntaxe scriptée :

      sed -n '
      /Je/ {
      :z
      N
      /suis/ !b z
      /titi/ p
      }
      ' toto
      

      Sinon, rajoute un "point virgule" avant l'accolade fermante :

      sed -n '/Je/{:z;N;/suis/!bz;/titi/p;}' titi
      
      0
  9. unixnewbie
     
    Comprend pas...

    La syntaxe scriptée fonctionne nickel mais pour la ligne de commande, même le "point virgule" avant l'accolade fermante ne résoud pas le pb.

    Pourtant une commande comme celle-là passe bien:
    sed -n '/Je/,/titi/p' titi
    0
    1. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
       
      Re-

      Quand je parle de syntaxe scriptée, je ne parle pas d'un script dans un fichier texte, mais bien en ligne de commande avec des retours charriots (<Entrée>) après chaque commande.

      $ sed -n ' <Entrée>
      > /Je/ { <Entrée>
      > :z <Entrée>
      > N <Entrée>
      > /suis/ !b z <Entrée>
      > /titi/ p <Entrée>
      > } <Entrée>
      > ' toto <Entrée>
      
      0
  10. dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   5 660
     
    hello
    essayer avec nawk
    $ cat a1 
    Je 
    m'appelle 
    toto 
    et 
    je 
    suis 
    gros 
    Je 
    m'appelle 
    titi 
    et 
    je 
    suis 
    petit  
    $ nawk '{x[NR]=$0}; /Je/ {a=NR}; /titi/ {b=NR}; /suis/ {if(b  > a && b < NR)for(n=a;n<=NR;)print x[n++]}'' a1 
    Je 
    m'appelle 
    titi 
    et 
    je 
    suis 
    $  
    
    0
  11. unixnewbie
     
    Avec un peu de retard (congés, toussa :-) ) : clap clap !

    Merci beaucoup. Ca fonctionne nickel !
    0
  12. unixnewbie
     
    Bonjour,

    Je reviens sur cette commande:
    La commande décrite ci-dessus fonctionne bien et je voudrais étendre son utilité pour qu'elle soit capable de me donner le même résultat sur plusieurs fichiers.
    En faisant le test, si je met un * à la place du fichier, ça marche mais je ne sais pas à quel(s) fichier(s) correspond(ent) les réponses. Ce n'est pas pratique.
    A contrario, si je fais un
    grep -l titi *
    je n'ai que les noms de fichier.

    Peux t'on mixer les deux ?

    Exemple de stdout que je voudrais avec une recherche nawk * sur un répertoire contenant 2 fichiers a1 et a2:

    a1:
    Je
    m'appelle
    titi
    et
    je
    suis

    a2:
    Je
    me nomme
    titi
    et
    je
    suis
    0
  13. dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   5 660
     
    essaye
    $ nawk 'FNR==1 {print FILENAME ":"}; {x[FNR]=$0}; /Je/ {a=FNR}; /titi/ {b=FNR}; /suis/ {if(b  > a && b < FNR)for(n=a;n<=FNR;)print x[n++]}' a1 a2
    0
  14. unixnewbie
     
    C'est ce que je disais, ça marche mais je n'ai pas de référence du fichier traité.
    Dans ce cas, la réponse est :
    Je
    m'appelle
    titi
    et
    je
    suis
    Je
    me nomme
    titi
    et
    je
    suis

    Ce n'est pas très lisible...

    Dans mon répertoire de travail, j'ai plusieurs centaines de fichiers sur lesquels je veux lancer ma recherche:
    nawk 'FNR==1 {print FILENAME ":"}; {x[NR]=$0}; /Je/ {a=NR}; /titi/ {b=NR}; /suis/ {if(b  > a && b < NR)for(n=a;n<=NR;)print x[n++]}' *
    0
  15. dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   5 660
     
    chez moi, il affiche bien a1: a2:
    c'est peut-être le nawk Solaris, essaye
    $ head a3 a4
    ==> a3 <==
    aaa
    
    ==> a4 <==
    bbb
    $ 
    $ nawk 'FNR==1 {print FILENAME ":"}; {print}' a3 a4
    a3:
    aaa
    a4:
    bbb
    $
    0
  16. unixnewbie
     
    Oups, je n'avais pas vu le FNR==1 {print FILENAME ":"};
    Avec, ça marche. Merci !

    Par contre, dans le cas d'une recherche sur plusieurs centaines de fichiers, il me donne le nom de chaque fichier scanné même s'il n' y a pas de correspondance.
    Penses-tu qu'il soit possible d'améliorer ce comportement ?

    J'ai essayé :
    nawk '{x[NR]=$0}; /Je/ {a=NR}; /titi/ {b=NR}; /suis/ {if(b  > a && b < NR)for(n=a;n<=NR;)print FILENAME ":" x[n++]}' *

    mais dans ce cas, chaque ligne correspondante est préfixée par le nom de fichier. Pas terrible...
    0
  17. dubcek Messages postés 18627 Date d'inscription   Statut Contributeur Dernière intervention   5 660
     
    essaye
    $ nawk 'FNR==1 {f=1}; {x[FNR]=$0}; /Je/ {a=FNR}; /titi/ {b=FNR}; /suis/ {if(b  > a && b < FNR)for(n=a;n<=FNR;){if(f){print FILENAME ":";f=0}print x[n++]}}' *
    0
  18. unixnewbie
     
    Je rentre seulement de congés et je me rends compte que j'ai oublié de remercier. Donc un grand merci !

    Maintenant, le pb, c'est que ça ne fonctionne pas parfaitement sur mes données.
    Dans le cas de l'exemple, titi/toto, ça fonctionne parfaitement mais dans le cas de mes données; ça me remonte un peu n'importe quoi sans que j'arrive à établir une corrélation.

    Dans la vie réelle, j'ai des fichiers XML avec une ou plusieurs commandes symbolisées par des numéros de contrat de type "LULUxxxxxxxxxx" (ou x sont des digits) et les balises <Commande> et </Commande>.
    Voici un exemple:

       <Commande>  
            <En-tete>  
                <Commande numCommande="26419" dateCommande="2010-04-09T14:26:05.000+02:00" typeCommande="M"/>  
                <ClientDemandeur codeClient="dfzesf"/>  
                <ClientFacture codeClient="fezf"/>  
                <ClientFinal codeClient="LULUxxxxxxxxxx" nom="efzfe" prenom="fezfgr"/>  
                <Acces idAcces="greg"  
                </Acces>  
            </En-tete>  
            <Detail>  
                <Service>  
                    <codeAction="C">  
                </Service>  
                <Equipement>  
                    <RTE numSequence="1"  
                </Equipement>  
            </Detail>  
        </Commande>  
    


    Lorsque je fais une recherche de ce type:
    nawk 'FNR==1 {f=1}; {x[FNR]=$0}; /<Commande>/ {a=FNR}; /LULUxxxxxxxxxx/ {b=FNR}; /<\/Commande>/ {if(b  > a && b < FNR)for(n=a;n<=FNR;){if(f){print FILENAME ":";f=0}print x[n++]}}' *

    la recherche me remonte n'importe quoi: des fichiers avec les mauvaises commandes, des fichiers avec plusieurs commandes dont celle que je recherche, des fichiers juste avec ma commande (le seul type de résultat que j'attend en fait)

    Si je fais la même recherche avec l'aide du grep:
    grep -l LULUxxxxxxxxxx * | xargs nawk 'FNR==1 {f=1}; {x[FNR]=$0}; /<Commande>/ {a=FNR}; /LULUxxxxxxxxxx/ {b=FNR}; /<\/Commande>/ {if(b  > a && b < FNR)for(n=a;n<=FNR;){if(f){print FILENAME ":";f=0}print x[n++]}}' 

    C'est mieux mais pas encore parfait: je retrouve des fichiers avec plusieurs commandes dont celle que je recherche et des fichiers juste avec ma commande

    Enfin, cette commande:
    grep -l LULUxxxxxxxxxx * | xargs nawk '{x[NR]=$0}; /<Commande>/ {a=NR}; /LULUxxxxxxxxxx/ {b=NR}; /<\/Commande>/ {if(b  > a && b < NR)for(n=a;n<=NR;)print x[n++]}' 

    me remonte les bonnes commandes mais il me manque les noms de fichiers auxquelles ces commandes correspondent.

    pour l'instant, seule la dernière commande est vraiment fiable mais sans l'info des fichiers, elle ne me sert pas à grand chose.

    Ouf! Je sais pas si je suis clair... Est-ce que vous pouvez encore m'aider ?
    0
    1. unixnewbie
       
      PS: Je ne sais pas si c'est plus simple mais un script bash ou sh qui ferait ça serait encore mieux.

      Si vous avez ça sous la main, merci !
      0