Forum Linux.débutant Comportement GREP différent dans le shell et dans un script

Posté par  . Licence CC By‑SA.
Étiquettes : aucune
0
3
mar.
2014

Bonjour,
Je suis très novice dans le monde linux.

Je cherche à remplacer une chaine de caractères dans plusieurs fichiers texte (+de 5000)
Ces fichiers sont générés par des machines de test, ils comportent 1 seule ligne.
La chaine à chercher est présente dans plusieurs fichiers ( 1 fichier par erreur rencontrée )

la chaine de remplacement est dans un fichier de type Liste.csv

    Chaine1;Remplace1
    Chaine2;Remplace2
    Chaine3;Remplace3
    Chaine4;Remplace4
    Chaine5;Remplace5
    ...
    Chainex;Remplacex

Sous Shell, si je lance le grep plusieurs fois de suite
j'ai le retour de la ligne voulue à chaque fois que je lance
grep -n Chaine1 Liste.csv

Dans un script AWK la même commande donne un résultat différent.
je récupère le résultat du grep dans une variable awk
grep -n ^Chaine1 Liste.csv | getline VariableAwk

Et la problème …
Seule la 1ère recherche donne un résultat.
si chaine1 est cherché une 2ème, 3ème fois, la variable reste vide.

Il semble que le grep ne lise qu'une fois le fichier Liste.csv
la 1ere fois il trouve la chaine en ligne 2,
la fois suivante, la recherche commence à la ligne 3 et donc ne trouve pas de correspondance.

Pouvez vous m'aider à résoudre ce problème
merci

EDIT

La syntaxe du script peut paraitre bizarre, mais pauvre de moi,
je suis sous MS-DOS avec les commandes GnuWin32.
les " et les ' n'ont pas tout à fait la même signification qu'avec un vrai shell.
c'est la raison des trop nombreux "print commande" du script.

J'ai tester avec la commande SED à la place GREP,
le problème est le même, seule la 1ère recherche est fructueuse,
les autres retournent des variable vides.

La commande lancée est

awk -f Script_Article_From_Liste.awk *.txt

Le script est (plein de "print" pour suivre à l'écran, ils seront supprimés)

BEGIN {
    FS=";";
    OFS=";";
    DATAFILE="Liste_Of-Article-Qty.csv"
    SNKEY="M"
    SNCNTR=100000
    print "----- START AWK -----------------------------------------------------"
    print "liste OF - Article : " DATAFILE
}

{
    # Lecture 1er fichier
    print ""
    print "---------------------------------------------------------------------"
    print "Fichier  : " FILENAME "  ||  " $0

    OFNUM=$1
    PARTNUM=$2
    SERNUM=$8

    # Chercher dans le fichier DATAFILE la chaine OFNUM avec Grep
    # !!! Ne fonctionne pas si 2 fois le meme OF cherche ...
    GREPCMD="GREP -n ^" OFNUM " " DATAFILE
    print "COMMANDE : " GREPCMD

    GREPCMD | getline DATALINE
    print "GREP DATALINE : " DATALINE

    # Chercher dans le fichier DATAFILE la chaine OFNUM avec SED
    # !!! Ne fonctionne pas si 2 fois le meme OF cherche ...
    # le head pour ne garder que la premiere occurence trouvee
    # SEDCMD="SED -s -n /" OFNUM "/p;q " DATAFILE | head -n 1"

    # print "COMMANDE : " SEDCMD
    # SEDCMD | getline DATALINE
    # print "DATALINE : " DATALINE

    # AJOUTER conditions d'erreurs !! 
    # DATALINE vide ...
    # 

    # Reformater les separateurs ( grep utilise : au lieu de ; )
    # Pas utile si SED utilis‚ … la place de GREP
    sub(":",";",DATALINE)

    # Changer DATALINE en tableau pour extraire les champs LINE / OF / NEWART / QTY
    split(DATALINE,DATATAB)
    print "DATATAB  : " "(1) " DATATAB[1] "  |  (2) " DATATAB[2] "  |  (3) " DATATAB[3] "  |  (4) " DATATAB[4]

    # Creer Numero de serie / Remplacer Code article
    SNCNTR=(SNCNTR + 1)
    $8=SNKEY SNCNTR "-" $2

    $2=DATATAB[2]
    PARTNUM=$2
    SERNUM=$8

    # Ligne Modifiee
    print "Fichier  : " FILENAME "  ||  " $0

    # Ecrire le fichier sur le disque
    # print $0 > FILENAME

    # Fermer les variables Locales ...
    DATALINE=""
    DATANUMLINE=""
    OFNUM="VIDE"
    PARTNUM="VIDE"
    SERNUM="VIDE"
    delete DATATAB      # Vider le tableau pour eviter les effets de bords
    close(DATAFILE)     # Fermer le fichier pour forcer la lecture depuis le début (ne marche pas!)
}

END{
    print ""
    print "----- FIN AWK ---------------------------------------------"
    print "Fichier(s) lu(s)        : " SNCNTR-100000
    print "Dernier Numero de serie : " SNKEY SNCNTR
    print ""
}

La liste de correspondance est (raccourcie)

No_OF;Article;Qté
1000014645;R8011450;250
5000000882;R7900085;247
1000014433;R800019;140
2000050803;R800020;136
5000000843;R800021;130

Les fichiers à traiter sont
AwkTest_01.txt

5000000882;000176A;sg;130927;105057;AOI_2;0.0.2;1;1;0;;

AwkTest_01b.txt (celui qui n'est pas vu … )

5000000882;000176B;sg;130927;105057;AOI_2;0.0.2;M100002-7654321;1;0;;

AwkTest_02.txt
sh
2000050803;000177A;sg;130927;105135;AOI_2;0.0.2;1;1;0;;

  • # Sed?

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

    Pour remplacer une chaîne de caractères dans un fichier, Sed fonctionne bien.

    Exemple :

    sed -i 's/old/new/g' *.txt
  • # Besoin de plus de contexte et de clarté

    Posté par  . Évalué à 3.

    Bon, j'ai moyennement compris ton problème, car les explications en français c'est moyen (« lancer plusieurs fois » : pour quelle raison ? avec quels arguments ? pour quel résultat ? ; tu as des fichiers d'une ligne, est-ce qu'ils correspondent à une « erreur » ? c'est quoi ces « erreurs » ?).

    Par contre, je peux te dire que c'est contre-productif de faire un grep dans awk : awk a des fonctions intégrées pour ça. Si tu veux faire quelque chose quand une ligne match, fait :

    /\^Chaine1/ { quelquechose }
    

    Ensuite, si tu veux faire « simplement » du remplacement, un bon vieux sed marche bien. Ton fichier de chaînes, d'ailleurs, tu pourrais en faire un script sed :

    #!/usr/bin/sed -e
    s/Chaine1/Remplace1/
    s/Chaine2/Remplace2/
    

    Etc. Et si tu veux vraiment que ça soit un CSV avec point virgule, tu pourrais même l'écrire ainsi :

    s;Chaine1;Remplace1;
    s;Chaine2;Remplace2;
    

    Et ton fichier tableur devient magiquement un script sed qui fait ce que tu veux.

    • [^] # Re: Besoin de plus de contexte et de clarté

      Posté par  . Évalué à 1.

      Merci de ce retour rapide.

      Les fichiers d'une lignes sont des rapports d'erreur de machines outils.
      Prendre une carte, la tester, à chaque erreur rencontrée écrire un fichier.

      Si la carte testée comporte 10 erreurs, il y aura 10 fichiers
      avec awk, j'ouvre chaque fichier, j’extraie la 1ere chaine
      Je traite ensuite tous les fichiers la contenant ( d'ou le grep )

      J'espère avoir été un peu plus clair …
      J'ai éditer le 1er post pour y placer les fichier en question.

      Merci de l'aide.

      • [^] # Re: Besoin de plus de contexte et de clarté

        Posté par  . Évalué à 1.

        Bon, je n'ai pas énormément plus compris. Au début tu parles de remplacement mais après tu ne remplaces jamais rien, et puis tu parles de « fichier » pour tes fichiers différents, alors que j'ai identifié au moins deux types différents (ceux, d'une ligne, qui contiennent l'erreur — d'ailleurs, parler de « première ligne » pour un fichier d'une ligne ajoute à la confusion — et celui qui a une tables de correspondance des remplacements).

        Ce que je vois, c'est que getline ne te renvoie qu'une ligne, donc oui forcément tu n'as que le premier match du grep/sed invoqué.

        En passant, j'ai l'impression que ton but plus général, ça ressemble à une jointure SQL. Si tu veux commencer à faire de la manipulation de donnée un peu avancée, tu ferais peut-être mieux d'importer tout ça en SQL et de créer des requêtes adéquates. Ça sera même plus clair pour toi je pense, au lieu de jouer du shell et des fichiers (c'est bien pour un petit script qui fait une tâche simple, mais ça devient vite limitant).

        Bonne chance.

        • [^] # Re: Besoin de plus de contexte et de clarté

          Posté par  . Évalué à 2.

          En passant, j'ai l'impression que ton but plus général, ça ressemble à une jointure SQL. Si tu veux commencer à faire de la manipulation de donnée un peu avancée, tu ferais peut-être mieux d'importer tout ça en SQL

          Hou la tu veux la mort de DOS toi ;) l'utilitaire join me parait plus approprié ;)

          Il ne faut pas décorner les boeufs avant d'avoir semé le vent

      • [^] # Re: Besoin de plus de contexte et de clarté

        Posté par  . Évalué à 3.

        logique tu n'as pas de boucle ;)

        il faudrait plutôt au niveau du getline avoir un
        while( ( GREPCMD | getline DATALINE) > 0 )
        {

        }

        Je fais ça de tête sans tester, faut peut être mettre le résultat de grep dans une variable intermédiare

        Il ne faut pas décorner les boeufs avant d'avoir semé le vent

      • [^] # Re: Besoin de plus de contexte et de clarté

        Posté par  (site web personnel) . Évalué à 1. Dernière modification le 03 mars 2014 à 22:33.

        Je ne suis pas sûr d'avoir tout compris, mais as-tu essayé ce que disait benoar?
        C'est-à-dire quelque chose comme

           sed -e 's/^/s;/;s/$/;/' Liste.csv > script.sed
        

        pour transformer ton fichier Liste.csv en un script sed. Et ensuite utiliser ce script pour faire les substitutions :

           sed -i -f script.sed *.txt
        
  • # Mes deux centimes

    Posté par  . Évalué à 4. Dernière modification le 03 mars 2014 à 19:25.

    À la lecture de ton post et des commentaires je vais te donner mon avis. Tu le mettras bien entendu à l'endroit de ta convenance !

    Jiehong et benoar ont raison. Tu dois utiliser sed, c'est fait, entre-autre, pour ça, remplacer une chaînes par une autre dans un fichier. grep lui, sert à filtrer les lignes d'un fichier selon un motif.

    awk est aussi une solution mais c'est sed ou bien awk, je ne crois pas qu'il y ait de cas réels ou théoriques où l'utilisation des deux en même temps dans un pipeline est pertinente. Je pose la question.

    fearan a, je pense, touché le nœud de ton problème. Il te manque une boucle !

    Par ailleurs,

    Il y a une syntaxe qui permet de rendre ton script lisible, utilise la à l'avenir, c'est indispensable lorsque tu colles un script, DOS ou Unix, même la copie des messages d'un terminal.

    Bienvenue à toi sur linuxfr.org.

Suivre le flux des commentaires

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