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 :

Problème variable AWK


Sujet :

Shell et commandes GNU

  1. #1
    Membre confirmé
    Inscrit en
    Juillet 2008
    Messages
    80
    Détails du profil
    Informations forums :
    Inscription : Juillet 2008
    Messages : 80
    Par défaut Problème variable AWK
    Bonjour,

    Dans une commane AWK, ma variable $14 correspond à une date.(jjmmaaaa)
    Je souhaiterai décomposer cette date dans trois variables $jj, $mm, $aa
    afin de pouvoir les mettre dans mon printf séparées par un ; pour qu'ils soient trois champs distincts.

    Commande faire la décomposition, j'ai essayé avec cut mais je n'y arrive pas, et sous quelle forme faut-ils les rajouter dans mon printf?

    D'avance merci pour votre aide!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    awk -F ';' -v out="$tabule/GF_PARCELLE_DATE.dat" '
    $14 ~ /3[0-1]02[0-9][0-9][0-9][0-9]/ {printf ("%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) >> out;}' $tabule/GF_PARCELLE.dat

  2. #2
    Membre émérite Avatar de jmelyn
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2007
    Messages
    703
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Septembre 2007
    Messages : 703
    Par défaut
    Bonjour,

    La commande awk possède la fonction split:

    split(chaine, tab, exp-reg) --> cette fonction découpe chaine en morceaux et les place dans tab suivant les séparateurs exp-reg. Mais si exp-reg est vide, chaque lettre occupe une case de tab.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    {split($14, tab, //); printf "%s%s;%s%s;%s%s%s%s\n", tab[1], tab[2], tab[3], tab[4], tab[5], tab[6], tab[7], tab[8]}
    Ce n'est sans doute pas la meilleure façon de faire, mais c'est la seule que j'ai trouvée pour l'instant...

    PS: c'est pour trouver les 30 et 31 février???

  3. #3
    Membre confirmé
    Inscrit en
    Juillet 2008
    Messages
    80
    Détails du profil
    Informations forums :
    Inscription : Juillet 2008
    Messages : 80
    Par défaut
    OK pour l'idée par contre, je n'ai pas de séparateur / entre les jours, mois , années. Ma date est sous la forme: 23102008 et non 23/10/2008.

    Y'a-t-il une autre commande?

  4. #4
    Membre confirmé
    Inscrit en
    Octobre 2008
    Messages
    30
    Détails du profil
    Informations forums :
    Inscription : Octobre 2008
    Messages : 30
    Par défaut
    Je ne comprends pas la remarque a propos du "/". C'est utile de connaître le format de ta date, mais il me semble que le code que l'on t'a donné est conforme à ton format.

    "//" définit un regex vide et non un caractère slash. Le regex est ce qui est écrit entre les deux slashs.

    Cyril.

  5. #5
    Membre confirmé
    Inscrit en
    Juillet 2008
    Messages
    80
    Détails du profil
    Informations forums :
    Inscription : Juillet 2008
    Messages : 80
    Par défaut
    En fait, si je fait : split($14, tab, //) ,toute la date se met dans tab[1], il n'y rien dans tab[2] ni tab[3] ...tab[8]

  6. #6
    Membre émérite Avatar de jmelyn
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2007
    Messages
    703
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Septembre 2007
    Messages : 703
    Par défaut
    Les caractères '/' dans la fonction split sont des méta-caractères, ils ne sont pas pris pour eux-mêmes, ils ont une signification spéciale utilisée dans les patterns pour délimiter les expressions régulières. Donc // veut dire "rien du tout".

    Pour résoudre ton problème, il serait bon que tu mettes une ligne du fichier de départ et comment elle doit être à l'arrivée.

    Edit: Oops, merci mcoolive, pas vu toutes tes interventions.

  7. #7
    Membre confirmé
    Inscrit en
    Octobre 2008
    Messages
    30
    Détails du profil
    Informations forums :
    Inscription : Octobre 2008
    Messages : 30
    Par défaut
    Comme je connais mieux sed, et que la question est assez simple, je donne la solution avec sed :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    echo "23102008" | sed 's/\([0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9][0-9][0-9]\)/\1;\2;\3/'
    # ou encore
    echo "24102008" | sed -r 's,([0-9]{2})([0-9]{2})([0-9]{4}),\1;\2;\3,'
    L'option "-r" est bien sympa pour écrireun peu moins d'anti-slash. Mais je crois qu'elle ne fonctionne pas pour toutes les versions de sed, donc des problèmes possibles pour faire fonctionner ton script sur des machins comme Solaris.

  8. #8
    Membre confirmé
    Inscrit en
    Juillet 2008
    Messages
    80
    Détails du profil
    Informations forums :
    Inscription : Juillet 2008
    Messages : 80
    Par défaut
    le fichier source est sous la forme suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    PA67669;APA67669;0;;;045;;;;33;1002;000000640;H00630;31092005;05215;0255;
    je veux récupérer le champ $14 qui correspond à une date érronée car il n'existe pas de 31 septembre
    ensuite je voudrais faire un printf pour que le fichier destination soit sous la forme: (remplacement du 31092005 par 30092005)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    PA67669;APA67669;0;;;045;;;;33;1002;000000640;H00630;30092005;05215;0255;;
    Le code que j'utilise pour les essais est le suivant, mais il m'affiche la date entière (tab[1]=31092005)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    awk -F ';' -v out="$tabule/GF_PARCELLE_DATE.dat" '
    $14 ~ /3111[0-9][0-9][0-9][0-9]/ {
    split($14,tab, //);
    printf ("%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, tab[1], $15, $16) >> out;}' $tabule/GF_PARCELLE.dat

  9. #9
    Membre émérite Avatar de jmelyn
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2007
    Messages
    703
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Septembre 2007
    Messages : 703
    Par défaut
    Encore une petite question:
    Le 31 septembre est mauvais. Dans le programme qui a créé cette date, est-ce le lendemain du 30 septembre? Je veux dire: aurait-il dû mettre la date du 1er octobre?

  10. #10
    Membre confirmé
    Inscrit en
    Juillet 2008
    Messages
    80
    Détails du profil
    Informations forums :
    Inscription : Juillet 2008
    Messages : 80
    Par défaut
    Je reçois le fichier d'une longueur de plus d'un million de ligne.
    C'est une erreur du fichier mais je ne peux pas avoir un fichier rectifié.
    Je dois le rectifier moi-même en remplacant par le 30 septembre

  11. #11
    Membre émérite Avatar de jmelyn
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2007
    Messages
    703
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Septembre 2007
    Messages : 703
    Par défaut
    Ma question est: cette date erronnée du 31 septembre est-elle créée pour le lendemain de 30 septembre? Devrait-elle donc être le 1er octobre?

    Je te pose cette question pour une raison bien précise: il est possible de corriger la date directement à l'intérieur de la commande awk. Et donc de vérifier toutes les dates et corriger automatiquement celles qui seraient fausses.

  12. #12
    Membre confirmé
    Inscrit en
    Juillet 2008
    Messages
    80
    Détails du profil
    Informations forums :
    Inscription : Juillet 2008
    Messages : 80
    Par défaut
    OK. Pour moi, on peut rectifier au premier jour d'avant ou d'après, celà n'a pas d'importance.

  13. #13
    Membre émérite Avatar de jmelyn
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2007
    Messages
    703
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Septembre 2007
    Messages : 703
    Par défaut
    Sinon il y a deux erreurs dans ce que tu as proposé:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    awk -F ';' -v out="$tabule/GF_PARCELLE_DATE.dat" '
    $14 ~ /3109[0-9][0-9][0-9][0-9]/ {
    split($14,tab, //);
    printf ("%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s%s;%s%s;%s%s%s%s;%s;%s;\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, tab[1], tab[2], tab[3], tab[4], tab[5], tab[6], tab[7], tab[8], $15, $16) >> out;}' $tabule/GF_PARCELLE.dat

  14. #14
    Membre confirmé
    Inscrit en
    Juillet 2008
    Messages
    80
    Détails du profil
    Informations forums :
    Inscription : Juillet 2008
    Messages : 80
    Par défaut
    Oui, pour l'exemple, pour le 09 tu as raison mais j'ai aussi des 11 (novembre)
    Pour le reste j'ai mis la formule avec juste tab[1] pour te dire que tab[1] ce n'est pas égale à 3 mais à la date complète 31092005

  15. #15
    Membre émérite Avatar de jmelyn
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2007
    Messages
    703
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Septembre 2007
    Messages : 703
    Par défaut
    J'espère que tu travailles sur un Linux récent.

    Voici donc la commande awk qui devrait normalement corriger toutes les dates erronnées en un seul passage:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    awk -F ';' '
    {
    	split($14, tab, //)
    	jour = tab[1] tab[2]
    	mois = tab[3] tab[4]
    	annee = tab[5] tab[6] tab[7] tab[8]
    	secondes = mktime(annee " " mois " " jour " 00 00 00")
    	date = strftime("%d%m%Y", secondes)
    	printf("%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, date, $15, $16)
    }' fichier_in > fichier_out

  16. #16
    Membre émérite Avatar de jmelyn
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Septembre 2007
    Messages
    703
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Septembre 2007
    Messages : 703
    Par défaut
    Je vais expliquer la commande awk utilisée pour que les forumeurs puissent la réutiliser facilement. Enfin, ceux qui ne la maîtrisent pas bien.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    awk -F ';' '
    {
    	split($14, tab, //)
    	jour = tab[1] tab[2]
    	mois = tab[3] tab[4]
    	annee = tab[5] tab[6] tab[7] tab[8]
    	secondes = mktime(annee " " mois " " jour " 00 00 00")
    	date = strftime("%d%m%Y", secondes)
    	printf("%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, date, $15, $16)
    }' fichier_in > fichier_out
    split($14, tab, //): la fonction split découpe le champ 14 en lettres et place les morceaux dans le tableau tab, puisque le séparateur (compris entre / et /) est vide. Donc tab[1] = 1ere lettre, tab[2] = 2e lettre... Les tableaux de awk commencent à l'indice 1, pas 0!

    jour = tab[1] tab[2]: avec awk, la concaténation est vraiment simple. Puisque $14 est de la forme JJMMAAAA, la variable jour récupère bien le jour du mois.

    Idem pour les deux variables suivantes, mois et année.

    secondes = mktime(annee " " mois " " jour " 00 00 00"): la fonction mktime() imite celle du C (voir man 3 mktime). En résumé, mktime() fait deux choses: elle normalise la date (par exemple: le 35 décembre 2008 est changée en 4 janvier 2009) puis elle transforme cette date normalisée en secondes écoulées depuis le 1er janvier 1970 à 00:00 (référence UNIX). C'est la meilleure manière de travailler avec les dates. Il faut noter les " " ajoutés parce que la concaténation de awk ne le fait pas automatiquement. La dernière partie du paramètre " 00 00 00" représente les heures, minutes et secondes dans le jour. Il est un peu risqué de tout mettre à zéro à cause des décalages été - hiver (DST = Day-light Saving Time), j'aurais donc dû mettre " 01 00 00" voire même " 12 00 00" pour plus de sécurité, même si théoriquement, il ne devrait pas y avoir de problème.

    date = strftime("%d%m%Y", secondes): Ici aussi, la référence est la fonction strftime() du C (man 3 strftime). La date en secondes est transformée en date humainement lisible. Le format est bien décrit dans la page man. Si le second paramètre (secondes) n'est pas fourni, c'est la date et l'heure de maintenant qui est utilisée.

    printf(...): la référence est également printf() du C (man 3 printf); la ligne sur laquelle awk travaille est ré-écrite telle quelle, sauf le champ 14 remplacé par la date normalisée. Il faut un '\n' à la fin, comme en C, sinon il n'y aura qu'une seule longue ligne.

    fichier_in > fichier_out: awk demande par défaut le fichier d'entrée à la fin de la commande. Inutile donc de faire passer ce fichier par stdin: awk ... < fichier_in. Et ce fichier n'est pas sur-écrit (printf ... >> 'fichier_in') parce qu'il est risqué de lire et d'écrire dans le même fichier, à moins que la commande soit expressément faite pour (comme sed -i).

    Remarque sur ce programme: Toutes les dates sont normalisées, qu'elles soient correctes ou non. Ainsi un seul passage est nécessaire pour avoir un fichier avec les bonnes dates.

Discussions similaires

  1. Problème variable.
    Par deviante dans le forum MFC
    Réponses: 3
    Dernier message: 27/01/2006, 17h27
  2. Problème variable et objet
    Par Hacken dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 07/09/2005, 14h10
  3. [javascript] Problème variable globale !!!!
    Par LE NEINDRE dans le forum Général JavaScript
    Réponses: 7
    Dernier message: 08/08/2005, 16h41
  4. [Rave report 5.1.3][delphi 7] Probléme variable PIVar
    Par GILLESKLEIN dans le forum Rave
    Réponses: 2
    Dernier message: 05/02/2005, 15h27
  5. problème variable extern
    Par HeKaz dans le forum C
    Réponses: 14
    Dernier message: 08/01/2003, 01h44

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