[Shell] If dans awk et histoire de NR

If dans awk et histoire de NR [Shell] - Shell/Batch - Programmation

Marsh Posté le 09-09-2009 à 12:56:58    

Bonjour,

 

j'ai un fichier .csv construit ainsi:

 
Citation :

numero1 prov1 dest1 prot1a prot1b
numero2 prov2 dest2 dest2a prot2b
numero3 prov3 dest3 prot3
numero4 prov4 dest4 prot4a prot4b prot4c prot4d
numero5 prov5 dest5 prot5
numero6 prov6 dest6 prot6

 

Il n'y a jamais plus de 9 champs par lignes.

 

Le but de mon petit script serait de le modifier ainsi:

 
Citation :

numero1 prov1 dest1 prot1a
numero1 prov1 dest1 prot1b
numero2 prov2 dest2 prot2a
numero2 prov2 dest2 prot2b
numero3 prov3 dest3 prot3
numero4 prov4 dest4 prot4a
numero4 prov4 dest4 prot4b
numero4 prov4 dest4 prot4c
numero4 prov4 dest4 prot4d
numero5 prov5 dest5 prot5
numero6 prov6 dest6 prot6

 

Le script que j'ai codé jusqu'ici a cette tête:

 
Code :
  1. awk '{
  2. if ( "$5" != 0)
  3. printf ($1";"$2";"$3";"$4";"$6";"$7";"$8";"$9"\n"$1";"$2";"$3";"$5";"$6";"$7";"$8";"$9"\n" )
  4. fi
  5. NR=NR-2
  6. }' fichier1 > fichier2;
 

Si la commande awk rencontre une ligne dont le 5e champ est non-nul alors la ligne est dupliquée selon le schéma ci-dessus. L'utilité du "NR=NR-2" est de "repasser" sur les lignes qui contiennent plus de 5 champs. C'est là où ça coince car cette dernière ligne ne fonctionne pas comme je le souhaiterai. Je suppose qu'il y a une subtilité, à moins que ça ne soit pas possible ?

 

Au final mon fichier2 est bien construit pour les lignes du fichier1 qui ne sont composées que de 5 champs au maximum. J'ai toutefois des lignes dans le fichier2 qui ont un 4e champ vide.


Message édité par lapin_vert le 09-09-2009 à 14:21:42
Reply

Marsh Posté le 09-09-2009 à 12:56:58   

Reply

Marsh Posté le 10-09-2009 à 16:26:02    

Problème résolu.
 
Vraiment bête comme solution, le plus simple n'arrive pas toujours en 1er place dans mon esprit :D

Code :
  1. #Lecture du fichier ligne par ligne en affichant que 4 champs par ligne
  2. awk '{
  3. printf ( $1" "$2" "$3" "$4"\n"$1" "$2" "$3" "$5"\n"$1" "$2" "$3" "$6"\n"$1" "$2" "$3" "$7"\n"$1" "$2" "$3" "$8"\n"$1" "$2" "$3" "$9"\n" )
  4. }' fichier1 > fichier2;
  5. #Suppression des lignes qui ne sont pas composées de 4 champs et qui ont donc un espace en fin de ligne
  6. sed -i '/ $/d' fichier2;


 
Y'a du "gâchis" en copiant de nombreuses lignes qui ne contiennent pas de champs 4.
 
Le problème qui reste est que si de nouvelles lignes avec + de 9 champs font leur apparition le script va bugger mais en compter le nombre de champ maximum rencontré lors de la lecture du fichier on peut arranger le 4e champ avec une variable associée au nombre maximum de champs.
 
Si c'est pas clair et que vous souhaitez + d'info repostez sur ce topic.

Reply

Marsh Posté le 10-09-2009 à 16:40:47    

Il y a une raison pour que ce soit en awk? En perl tu pourrais probablement avoir un truc qui marche sans limitation du nombre de champs.
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 10-09-2009 à 16:51:31    

gilou a écrit :

Il y a une raison pour que ce soit en awk? En perl tu pourrais probablement avoir un truc qui marche sans limitation du nombre de champs.
A+,


En effet j'aurai très bien pu faire ça en perl. Mais je me suis jamais penché sur ce langage. Je pensai que c'était un gain de temps de ne pas utiliser un langage inconnu pour moi et de me concentrer sur mes acquis si je voulais finir rapidement mon script.
 
Je sens que je vais quand même y jeter un oeil la prochaine fois. On se donne RDV dans la section perl :o :D

Reply

Marsh Posté le 11-09-2009 à 16:19:38    

Pour parser des données formattées comme suit:

numero1;prov1;dest1;prot1a;prot1b
numero2;prov2;dest2;dest2a;prot2b
numero3;prov3;dest3;prot3
numero4;prov4;dest4;prot4a;prot4b;prot4c;prot4d
numero5;prov5;dest5;prot5
numero6;prov6;dest6;prot6


j'utiliserais ceci:

Code :
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use Text::CSV;
  5.  
  6. my $sep = ';';
  7. my $csv = new Text::CSV({sep_char => $sep});
  8. my @f;
  9. while (<> ) {
  10.    if($csv->parse($_)) {
  11.        @f = $csv->fields();
  12.        if (scalar(@f) > 3) {
  13.            print join("\n", map {$f[0].$sep.$f[1].$sep.$f[2].$sep.$_} @f[3..scalar(@f)-1]), "\n";
  14.        }
  15.    }
  16. }


a la ligne 7, je crée un nouveau parser au format CSV
a la ligne 9, je récupère stdin ligne a ligne
a la ligne 10 et 11: une ligne est parsée et ses champs sont rangés dans un array
a la ligne 12 et 13: s'il y a plus de trois champs, je crée un sous-array qui démarre a partir du 4e champ, je concatène les 3 premiers champs de l'ancien array a chaque champ du sous-array, et j'imprime chaque champ ainsi modifié ligne à ligne.
 
Pour la ligne en entrée numero1;prov1;dest1;prot1a;prot1b  
après parsing, @f est un array a 5 éléments (numero1, prov1, dest1, prot1a, prot1b)
@f[3..scalar(@f)-1]) vaut donc (prot1a, prot1b)
$f[0].$sep.$f[1].$sep.$f[2].$sep vaut "numero1;prov1;dest1;"
map {$f[0].$sep.$f[1].$sep.$f[2].$sep.$_} @f[3..scalar(@f)-1] transforme donc  
(prot1a, prot1b) en (numero1;prov1;dest1;prot1a, numero1;prov1;dest1;prot1b) et il ne reste plus qu'a imprimer chaque élément de cet array comme une ligne, par du code standard (print join '\n',...).
A+,


Message édité par gilou le 11-09-2009 à 16:27:25

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 11-09-2009 à 16:34:22    

Code :
  1. awk 'NF>3{for (i=4;i<=NF; ++i) {print $1 " " $2 " " $3 " " $i}}' test.txt


Si j'ai bien compris [:doc petrus]

Reply

Marsh Posté le 11-09-2009 à 16:40:19    

Excellent ça. Ça fait trop longtemps que je n'ai plus touché à awk pour me souvenir de sa syntaxe :o  
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 11-09-2009 à 17:32:22    

Merci gilou pour ces explications. :jap:
 
Et merci erulio ta commande fonctionne impec' :love:  
 
Edith: c'est le NF>3 qui ne m'était pas venu à l'esprit. Ca fonctionne comme un if en fait ?  [:petrus75]

Message cité 1 fois
Message édité par lapin_vert le 11-09-2009 à 17:33:35
Reply

Marsh Posté le 11-09-2009 à 17:45:44    

lapin_vert a écrit :

Edith: c'est le NF>3 qui ne m'était pas venu à l'esprit. Ca fonctionne comme un if en fait ?  [:petrus75]


C'est sans doute strictement identique à un if(NF<3) placé correctement. Donc en fait non, ça devait pas te gêner tant que ça :D

Message cité 1 fois
Message édité par erulio le 11-09-2009 à 17:47:26
Reply

Marsh Posté le 11-09-2009 à 17:55:17    

erulio a écrit :


C'est sans doute strictement identique à un if(NF<3) placé correctement. Donc en fait non, ça devait pas te gêner tant que ça :D


Ouai enfin y'a une marge entre ton  

Code :
  1. awk 'NF>3{for (i=4;i<=NF; ++i) {print $1 " " $2 " " $3 " " $i}}' test.txt


et mon

Code :
  1. awk '{
  2. if ( "$5" != 0)
  3. printf ($1";"$2";"$3";"$4";"$6";"$7";"$8";"$9"\n"$1";"$2";"$3";"$5";"$6";"$7";"$8";"$9"\n" )
  4. fi
  5. NR=NR-2
  6. }' fichier1 > fichier2;


 
:o

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed