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 :

Arguments par lecture de fichier


Sujet :

Shell et commandes GNU

  1. #1
    Membre à l'essai
    Homme Profil pro
    Ingénieur
    Inscrit en
    Avril 2015
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur

    Informations forums :
    Inscription : Avril 2015
    Messages : 21
    Points : 23
    Points
    23
    Par défaut Arguments par lecture de fichier
    Bonjour
    je suis en shell linux. Ca fait des heures que je cherche sur les forums (comme ici : http://www.developpez.net/forums/d10...ourir-fichier/ ) et que je tente des trucs, mais rien n'y fait. Ca semble assez simple pourtant.

    Pour expliquer : mon répertoire contient 10 fichiers .txt qui doivent être passés à la moulinette d'un prog.awk. A cela il faut ajouter 2 arguments col1 et col2. Le problème est que les valeurs de col1 et col2 - dans le détail col1 est un string et col2 un nombre - doivent être récupérés d'un tierce fichier (param.dat) qui a 10 lignes par 3 colonnes (la 3e colonne est inusitée). Il faudrait donc pouvoir lire, à chaque boucle de for, la i-ième ligne du param.dat, et récupérer la colonne1 pour le premier argument d'awk, et la colonne2 pour le second. J'avais pensé à lire le param.dat avant la boucle for et le stocker en 3 arrays de colonnes.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #!/bin/sh
    while read -a col1 col2 col3; do ; done < param.dat
    for fichier in *.txt ; do
    	i=`expr $i + 1`
    	awk -v arg1=${col1[$i]} -v arg2=${col2[$i]} -f prog.awk $fichier
    done
    Je suis pas fort en shell. Une idée ? Merci d'avance

  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 389
    Points
    19 389
    Par défaut
    Bonjour,

    t'es obligé de faire ça en shell POSIX (/bin/sh)? parce que POSIX n'implémente pas les tableaux.
    bon, en même temps, l'emploi d'un tableau ne semble pas pertinent ici.

    je résume :
    Code pseudo-code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    pour chaque_ficher d'un répertoire_donné
       autant de fois qu'il y a de lignes dans un fichier_data
          retenir les deux premiers champs
          traiter chaque_fichier avec awk et les deux premiers champs
       fin_autant
    fin_pour
    c'est ça ?
    .
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  3. #3
    Expert éminent Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 035
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 035
    Points : 8 400
    Points
    8 400
    Par défaut
    salut,

    j'ajouterais à ce que dit N_BaH que awk -f prog.awk *.txt marche aussi, tu n'as peut-être pas besoin de boucle for au final

  4. #4
    Membre à l'essai
    Homme Profil pro
    Ingénieur
    Inscrit en
    Avril 2015
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur

    Informations forums :
    Inscription : Avril 2015
    Messages : 21
    Points : 23
    Points
    23
    Par défaut
    Citation Envoyé par N_BaH Voir le message
    Bonjour,

    t'es obligé de faire ça en shell POSIX (/bin/sh)? parce que POSIX n'implémente pas les tableaux.
    bon, en même temps, l'emploi d'un tableau ne semble pas pertinent ici.

    je résume :
    Code pseudo-code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    pour chaque_ficher d'un répertoire_donné
       autant de fois qu'il y a de lignes dans un fichier_data
          retenir les deux premiers champs
          traiter chaque_fichier avec awk et les deux premiers champs
       fin_autant
    fin_pour
    c'est ça ?
    Hello,
    non, je peux mettre bash ou csh (je suis de la génération unix 90, et à l'époque je crois que c'était ksh ). Ton code est presque ça. Je reprends :
    Code pseudo-code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    pour chaque_ficher d'un répertoire_donné
       récupérer 1 seul ligne dans un fichier_data (la n-ème, dans l'ordre avec le n-ème chaque_fichier)
          retenir les deux premiers champs
          traiter chaque_fichier avec awk et les deux premiers champs
       fin_récupérer
    fin_pour

    évidemment, si on n'arrive pas à "isoler" la n-ème et qu'on doit, pour chaque itération for, relire tout le fichier_data et ne prendre que sa n-ième ligne pour les 2 premiers champs, c'est pas grave (le fichier est court)

  5. #5
    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 458
    Points
    13 458
    Par défaut
    Bonjour,

    pour moi, tout peut être traité avec un seul petit script awk.
    Pour avancer, peux-tu donner un échantillon des fichiers de départ et le résultat que tu veux obtenir ?
    Cette réponse vous apporte quelque chose ? Cliquez sur en bas à droite du message.

  6. #6
    Membre à l'essai
    Homme Profil pro
    Ingénieur
    Inscrit en
    Avril 2015
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur

    Informations forums :
    Inscription : Avril 2015
    Messages : 21
    Points : 23
    Points
    23
    Par défaut
    en effet, j'avais tenté de tout faire sur awk; avec un getline appelant un 2e fichier, mais cette commande est trop bizarre (elle efface les champs $ du fichier principal, et ça saute d'une ligne, bref le foutoir ...). Mon programme est assez simple :

    prog.awk
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    BEGIN {tablename="IW_FS"}
    {
    if (cnt==0) {cnt++; print arg1 > ARGV[1]".asc"; next} # ca ecrit dans un nouveau nom concaténé en appelant le nom du fichier
    if (cnt==1) {cnt++; print arg2 > ARGV[1]".asc"; next}
    if (cnt==2) {cnt++; print tablename > ARGV[1]".asc"; next}
    printf("%5d   %7.2f\n",$1,$2) > ARGV[1]".asc"    
    }

    param.dat
    SUG-0001 623 KB
    SUG-0004 614.2 KB
    SUG-0005 614 KB
    ... (10 lignes)


    fichier_01.txt (10 fichiers de ce genre, ordonnés en cohérence avec le param.dat)
    # ORIGIN=460
    # STEP=20
    # REGULAR COORDS
    460 52.219
    480 55.9167
    500 59.6082
    520 63.7675
    540 67.628
    560 71.5186
    580 75.5668
    ..... (plein de lignes)

    et le code serait pour le premier fichier isolé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk -v arg1=SUG-0001 -v arg2=623 -f prog.awk fichier_01.txt
    pour le second :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk -v arg1=SUG-0004 -v arg2=614.2 -f prog.awk fichier_02.txt
    etc ...

  7. #7
    Expert éminent sénior Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 280
    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 280
    Points : 12 729
    Points
    12 729
    Par défaut
    Bonjour,

    Flodelarab à raison, awk est suffisant pour faire le tout, voici des pistes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    FNR == NR {A[FNR]=$1;B[FNR]=$2;next}
    ici on stock dans le tableau A le champs 1 et dans le tableau B le champs 2 tant que l'on lit le premier fichier (donc dans ton cas ce sera le param.dat)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    FNR == 1 {X++}
    {arg1=A[X];arg2=B[X];....}
    FNR redémarre à chaque nouveau fichier, donc on incrémente l'index X (que l'on aura initialise à 0 dans BEGIN pour être propre) à chaque fois que FNR est égale à 1 (pour le premier fichier qui est le param.dat, il ne passera pas par cette étape grâce à la clause next.
    Cordialement.

  8. #8
    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 458
    Points
    13 458
    Par défaut
    Je plussoie cette excellente piste. Le script sera appelé de cette façon (par exemple):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk 'script' param.dat fichier_[0-9][0-9].txt
    Cette réponse vous apporte quelque chose ? Cliquez sur en bas à droite du message.

  9. #9
    Membre à l'essai
    Homme Profil pro
    Ingénieur
    Inscrit en
    Avril 2015
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur

    Informations forums :
    Inscription : Avril 2015
    Messages : 21
    Points : 23
    Points
    23
    Par défaut
    waw, merci diserdogue. Je viens de gagner 5% d'XP en awk
    En fait je découvre que l'on peut mettre 2 fichiers en arguments d'awk (et qu'il les traitera l'un après l'autre), et segundo ce que veut dire la variable FNR ! Cool.

    Sinon entre temps j'avais continué des essais en shell, et je suis presque arrivé ! :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    while read -a Param; do
                i=`expr $i + 1`
                awk -v arg1=${Param[0]} -v arg2=${Param[1]} -f prog.awk  $fichier$i.txt
    done < param.dat
    Le seul problème est que je sais pas écrire correctement ce genre de concatenation de nom fichier+ $i + .txt, en plus la galère quand ça passe de 09 à 10 ... Donc en résumé vive awk !
    Merci à tous

  10. #10
    Expert éminent sénior Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 280
    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 280
    Points : 12 729
    Points
    12 729
    Par défaut
    Voici quelques exemples de concaténation de variable en shell (le '$' en début de chaque ligne est mon prompt):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ foo=bar
    $ i=1
    $ echo "${foo}${i}"
    bar1
    $ echo "${foo}${i}.txt"
    bar1.txt
    $ printf "%s%02d.txt\n" ${foo} ${i}
    bar01.txt
    $ ((i+=10)) # Pas compatible avec tous les shell, ici je suis en bash
    $ printf "%s%02d.txt\n" ${foo} ${i}
    bar11.txt
    Cordialement.

  11. #11
    Expert éminent Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 035
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 035
    Points : 8 400
    Points
    8 400
    Par défaut
    salut,

    en s'appuyant sur le principe expliqué par disedorgue plus haut et full awk :
    Code awk : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    BEGIN {
            tablename = "IW_FS";
    }
    FNR == NR {  # on tire parti du mécanisme, le bloc ne sera exécuté que pour le 1er fichier (param.dat)
            f=ARGV[NR+1]; # et on se base sur le compteur NR pour remplir nos tableaux associatifs de col1/col2
            col1[f] = $1;
            col2[f] = $2;
            printf ("%s\n%s\n%s\n", col1[f], col2[f], tablename) > f".asc"; # on ecrit les 3 lignes d'un coup
            next;
    }
    !/^#/ {  # ici on rajoute une clause pour eviter de traiter les commentaires dans les fichiers .txt
            printf ("%5d   %7.2f\n", $1, $2) >> FILENAME".asc" # puis le reste, ligne apres ligne...
    }

    comme je te disais plus haut tompaspro tu n'as pas besoin de boucle for/while, ici awk s'en charge pour nous
    d'où l'invocation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk -f prog.awk param.dat *.txt

  12. #12
    Membre à l'essai
    Homme Profil pro
    Ingénieur
    Inscrit en
    Avril 2015
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur

    Informations forums :
    Inscription : Avril 2015
    Messages : 21
    Points : 23
    Points
    23
    Par défaut
    Salut BufferBob,

    belle concision, mais il doit y avoir qq petites erreurs (dont des accolades). J'aurais donc réécrit ton code en :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    BEGIN {tablename="IW_FS"}
    {
    if (FNR==NR) 
    	{ printf("%s\n%s\n%s\n",$1,$2,tablename) > ARGV[NR+1]".txt"}
    else  { printf("%5d   %7.2f\n",$1,$2) > FILENAME".txt" }
    }
    Par contre je ne comprends pas le !/^#/ , où doit-il se mettre ? Car là ca marche pas et il lit bien les 3 premières lignes

  13. #13
    Expert éminent Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 035
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 035
    Points : 8 400
    Points
    8 400
    Par défaut
    Citation Envoyé par tompaspro Voir le message
    il doit y avoir qq petites erreurs
    tu l'as testé ?

  14. #14
    Membre à l'essai
    Homme Profil pro
    Ingénieur
    Inscrit en
    Avril 2015
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur

    Informations forums :
    Inscription : Avril 2015
    Messages : 21
    Points : 23
    Points
    23
    Par défaut
    en effet ... ça marche

    j'ai même pu enlever le next, il marche aussi.
    Mais je comprends toujours pas. Je pensais qu’après l'introduction BEGIN, il fallait ouvrir l'accolade et écrire le script. De plus, si je mets en plus "lisible" avec un if (FNR==NR), ça ne marche pas. Comment expliquer ces différences ? Comment il interprète cette condition avec cette syntaxe-là ? Ou est le else ?

    Et pour le !/^#/ , j'imagine que je peux le placer juste après le BEGIN et l'accolade principale de tout script awk ? Ce qui est étrange est que je dois mettre l'accolade entrante sur la même ligne, juste après, sans pouvoir retourner_chariot avant d'écrire l'accolade. Ca doit être un code caché des MM. A.W.K

  15. #15
    Expert éminent Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 035
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 035
    Points : 8 400
    Points
    8 400
    Par défaut
    Citation Envoyé par tompaspro Voir le message
    j'ai même pu enlever le next, il marche aussi.
    je ne pense pas, ça doit générer un fichier en plus du nom de param.dat.asc qui n'a rien à faire là

    Je pensais qu’après l'introduction BEGIN, il fallait ouvrir l'accolade et écrire le script. De plus, si je mets en plus "lisible" avec un if (FNR==NR), ça ne marche pas. Comment expliquer ces différences ? Comment il interprète cette condition avec cette syntaxe-là ? Ou est le else ?
    ben justement, tu ne mets pas if (BEGIN), le mot-clé BEGIN sert de condition, comme END ou /regex/ ou encore FNR == NR

    awk fonctionne sur une syntaxe assez simple de la forme condition {instructions}, sur cette condition il y a comme un if implicite si tu préfères, en revanche pas de else, on est obligé de tourner la chose différement (on aurait pu en effet mettre la condition dans un if(), mais ça ne nous arrangeait pas ici)

    il faut comprendre un peu le code, hormis le bloc BEGIN (qu'on pourrait largement faire sauter à mon avis), le second bloc n'est exécuté QUE lorsque awk traite les lignes du 1er fichier passé en paramètre, ici param.dat, c'est ce que disaient disedorgue et Flodelarab.
    à l'inverse, le troisième bloc n'est exécuté QUE lorsque awk traite les lignes qui n'appartiennent pas au premier fichier (donc tous les autres, ici fichier_*.txt), le principe est assez simple;

    durant le traitement des fichiers en paramètres, NR ira de 1 à 10 pour le premier fichier, puis de 1 à 542 pour fichier_01, puis de 1 à 818 pour fichier_02 etc. tandis que FNR ira de 1 à 3807 sans jamais être réinitialisé

    du coup le seul moment où FNR == NR c'est quand awk traite le tout premier fichier (param.dat), ce qui nous permet de lui appliquer un traitement spécial

    et tandis qu'on traite ce fichier, la variable NR nous permet elle d'itérer sur le prochain fichier passé en paramètre, en clair :
    • à la première ligne de param.dat, NR vaut 1, ARGV[NR+1] vaut fichier_01.txt qui est le 2e paramètre
    • à la deuxième ligne de param.dat, NR vaut 2, ARGV[NR+1] vaut fichier_02.txt qui est le 3e paramètre
    • à la troisième ligne de param.dat, NR vaut 3, ARGV[NR+1] vaut fichier_03.txt qui est le 4e paramètre
    • etc.

    on comprend alors que le next a son utilité, pour éviter que le troisième bloc soit exécuté lorsque awk traite param.dat, il faut l’empêcher de continuer son traitement à l'intérieur du second bloc.

    Et pour le !/^#/
    comme expliqué plus haut c'est une condition, ici sous forme d'une expression régulière
    • /^#/ sert à matcher (et donc rentrer ou non dans le bloc) si la ligne commence par un # (les commentaires donc)
    • le ! devant permet la négation


    Edith: je m'en suis rendu compte en l'écrivant, pas besoin de tableau associatif en fait
    Code awk : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    FNR == NR {
       printf ("%s\n%s\n%s\n", $1, $2, "IW_FS") > ARGV[NR+1]".asc";
       next;
    }
    !/^#/ {
       printf ("%5d   %7.2f\n", $1, $2) >> FILENAME".asc"
    }

Discussions similaires

  1. Lecture de fichier ligne par ligne
    Par chreks dans le forum Fortran
    Réponses: 4
    Dernier message: 19/05/2008, 18h09
  2. Lecture de fichier ligne par ligne avec caractères spéciaux
    Par Australia dans le forum Shell et commandes GNU
    Réponses: 3
    Dernier message: 28/11/2007, 15h30
  3. Lecture de fichiers wave par événements extérieurs
    Par Jean Breil dans le forum Pascal
    Réponses: 0
    Dernier message: 19/09/2007, 00h19
  4. Lecture de fichier par blocs
    Par nicolas66 dans le forum C++
    Réponses: 12
    Dernier message: 11/11/2006, 20h36
  5. Lecture de fichiers images pixels par pixels
    Par FabHacker dans le forum Langage
    Réponses: 3
    Dernier message: 26/11/2005, 16h12

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