[BASH / AWK] Modification complexe de champ

Résolu/Fermé
Nicooo - 2 févr. 2010 à 10:54
 Nicooo - 3 févr. 2010 à 16:29
Bonjour à la communauté,

Je sèche lamentablement sur le problème suivant, est-ce que l'un d'entre vous aurait une solution ou à défaut une piste à proposer ?

LE FICHIER D'ENTREE (77 champs, 3 lignes pour l'exemple) :

1,2,42698,44111624,1211889827,2,0,-516943680,"95011","95011",0,17,4,0,0,0,0,0,0,0,0,0,0,"0","0",44111628,2,0,48238784,"54790","98500004","",0,0,4,0,0,0,0,0,0,0,0,0,0,"0","0",0,1211889828,"54790","0aaaa261-d245-4264-b09d-6ac17129a5e4","PBX_FR-PBX","_Globals_DNs","_Globals_DNs","PBX_FR-PBX",0,"SEP001E4AAAAAA7","IPMA-RP",12,0,1,1,18,18,0,"Cluster1",1,"","",0,"",3,1,0,0,0,0,""
1,2,42699,44111631,1211889846,2,0,-516943680,"95011","95011",0,1,4,0,0,0,0,0,0,0,0,0,0,"0","0",44111632,0,0,0,"54576","54576","",0,0,4,0,0,0,0,0,0,0,0,0,0,"0","0",0,1211889847,"54576","558aaaa5-225c-4f4c-a4df-9181e09c2f1f","","_Globals_DNs","","",0,"SEP001E4AAAAAA7","",13,0,0,0,0,0,0,"Cluster1",0,"","",0,"",3,0,0,0,0,0,""
1,2,42696,44111613,1211889674,2,0,-887353152,"49240","49240",0,0,4,-887353152,18454,4,20,0,0,0,0,0,0,"0","0",44111614,2,30,320733888,"00142148396","00142148396","",0,16,4,320733888,18380,4,20,0,0,0,0,0,0,"0","0",1211889677,1211889868,"00142148396","0abaaaa7-2990-48df-81d7-e9d015186857","PBX_PSTN-FR-Granite","_Globals_DNs","PBX_PSTN-FR-Granite","PBX_PSTN-FR",191,"SEP001E7ACAAAA1","S0/SU0/DS1-1@DEF-C50",0,12,0,0,0,0,0,"Cluster1",0,"","",0,"",3,1,0,0,64,64,""

LE BESOIN :
- modifier les champs 5, 48, 49, qui contiennent une date/heure en epoch, en heure lisible du type dd/mm/yyyy hh:mm, et ce pour chaque ligne de données
- modifier les champs 8, 14, 22, 29, 36, 44, qui contiennent une adresse IP en unsigned integer MAIS stockée en signed integer, en adresse ip lisible du type abc.def.ghi.jkl
petite précision/exemple sur la manip de conversion :
- on a les adresses ip suivantes : -1139627840 , 320733888
- on convertit en hexadécimal : 0xBC12A8C0 (bon en fait 0xFFFFFFFBC12A08C0) , 131e02c0
- on inverse par paires comme suit : C0 A8 12 BC , C0 02 1E 13
- on convertit en décimal par paires : 192. 168 18 188 , 192.2.30.19

LES PISTES ENVISAGEES :

- Awk, car ça semble bien adapté à la modification rapide de champ dans un fichier. Mais je suis pas sectaire ;), donc si une solution intelligente existe en bash, je prends. je souhaite eviter perl car il n'est pas installé sur la machine de prod que j'utilise.

- pour la première modification (epoch => heure lisible) :
cat fichier | awk -F"," '{$5 = strftime("%F %X", $5) ; $48 = strftime("%F %X", $48) ;$49 = strftime("%F %X", $49) ;print $0}' donne un résultat ok

- pour la deuxième modification (unsigned int => @ ip) : j'arrive à convertir (mais ce n'est pas très élégant, et je n'arrive pas à le faire pour toute les lignes) ainsi
- pour les chiffres positifs : echo 320733888 | awk '{printf("%x\n",$1)}' | awk --non-decimal-data 'BEGIN {FIELDWIDTHS = "2 2 2 2"}{ print ("0x"$4)+0 "." ("0x"$3)+0 "." ("0x"$2)+0 "." ("0x"$1)+0 }'
- pour les chiffres négatifs : echo "-1139627840" | awk '{printf("%x\n",$1)}' | awk --non-decimal-data 'BEGIN {FIELDWIDTHS = "8 2 2 2 2"}{ print ("0x"$5)+0 "." ("0x"$4)+0 "." ("0x"$3)+0 "." ("0x"$2)+0 }'

Si vous avez des idées, je suis preneur !!
En vous remerciant,

Nico.
A voir également:

8 réponses

lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 569
2 févr. 2010 à 10:57
Salut,

je souhaite eviter perl car il n'est pas installé sur la machine de prod que j'utilise.
Ta machine de prod as un os GNU/Linux ou Unix?
Si oui alors perl est installé ;-)

0
/usr/bin/perl : ha, oui tiens ^^
par contre je maitrise pas un poil ce langage T_T
0
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 569
2 févr. 2010 à 11:10
Re,

Je n'ai pas le temps pour me pencher sur ton problème puisque je suis au boulot.
Si entre temps tu n'auras pas une réponse (je pense à dubcek ou jipicy ;-) mais ça peut être n'importe qui d'ailleurs ;-) ) alors je vais regarder ce soir.

0
merci beaucoup !! :)
0
dubcek Messages postés 18755 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 14 novembre 2024 5 621
2 févr. 2010 à 14:33
hello
- le fichier a1 contient les lignes d'exemple
- avec split("8,14,22,29,36,44",x,",") on initialise un tableau x contenant les No de champs qu'on veut traiter
- est ce une adresse valide ? 48238784 == 0.12.1.46
$ echo -1139627840  320733888  | awk '{for(i=1;i<=NF;i++){ip=sprintf("%x", $i);printf("%d.%d.%d.%d ", "0x" substr(ip,7,2), "0x" substr(ip,5,2), "0x" substr(ip,3,2), "0x" substr(ip,1,2))};print ""}' 
192.168.18.188 192.2.30.19 
$ 
$ awk -F, '{print $8, $14, $22, $29, $36, $44}' a1
-516943680 0 0 48238784 0 0
-516943680 0 0 0 0 0
-887353152 -887353152 0 320733888 320733888 0
$ 
$ awk -F, 'BEGIN{split("8,14,22,29,36,44",x,",")};{for(i in x) {ip=sprintf("%x", $(x[i]));printf("%d.%d.%d.%d ", "0x" substr(ip,7,2), "0x" substr(ip,5,2), "0x" substr(ip,3,2), "0x" substr(ip,1,2))};print ""}' < a1 
192.16.48.225 0.0.0.0 0.0.0.0 0.12.1.46 0.0.0.0 0.0.0.0 
192.16.48.225 0.0.0.0 0.0.0.0 0.0.0.0 0.0.0.0 0.0.0.0 
192.16.28.203 192.16.28.203 0.0.0.0 192.2.30.19 192.2.30.19 0.0.0.0 
$ 
0
Bonjour Dubcek,

Merci pour ta réponse et désolé pour le retour tardif. Je pense comprendre le principe de ce que tu proposes, néanmoins chez moi ça retourne uniquement des 0.0.0.0.
Après un peu de tests :
netprd04@netprdadm02#echo -1139627840 | awk '{ip=sprintf("%x", $1); print(ip); print(substr(ip,15,2)); printf("%d ", "0x" substr(ip,15,2)); print("")}'
retourne :
ffffffffbc12a8c0
c0
0

il y a un problème avec la conversion hexa vers decimal, mais je n'arrive pas à voir d'où ça vient...
Je ne comprends pas non plus pourquoi chez toi les substr sur les digits 7, 5, 3, 1 fonctionnent à la fois fois sur les nombres positifs (8 digits en hexa chez moi) et négatifs (16 digits en hexa chez moi).
ma version de awk est GNU AWK 3.1.5, peut-être est-ce que ça vient de là ?

Merci pour ton intérêt pour mon problème en tout cas !

Nicooo.
0
dubcek Messages postés 18755 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 14 novembre 2024 5 621
3 févr. 2010 à 09:49
j'utilise le awk traditionnel, effectivement gawk agit différement.
essaye gawk avec --posix
essaye avec mawk, le awk traditionnel
0 
$ echo -1139627840 | gawk --posix '{ip=sprintf("%x", $1); print(ip); print(substr(ip,15,2)); printf("%d ", "0x" substr(ip,15,2)); print("")}' 
ffffffffbc12a8c0
c0
192 
0
hello,

j'ai essayé avec :
- gawk --posix ou gawk -W posix : je tombe sur le même résultat
- mawk : inconnu, je ne le trouve pas en faisant find / -name *awk*

A priori tous les awk, gawk, pgawk, igawk de /bin ou /usr/bin sont de la même version (3.1.5).

Si j'utilise l'option que j'avais trouvé sur le net (--non-decimal-data), ça tourne à peu près :
netprd04@netprdadm02#echo -1139627840 320733888 | awk --non-decimal-data '{for(i=1;i<=NF;i++){ip=sprintf("%x", $i);printf("%d.%d.%d.%d ", "0x" substr(ip,7,2), "0x" substr(ip,5,2), "0x" substr(ip,3,2), "0x" substr(ip,1,2))};print ""}'
255.255.255.255 192.2.30.19

Par contre comme les conversions de nombres négatifs sont sur 16 chiffres chez moi, je vais être obligé d'utilsier une structure conditionnelle je pense, à moisn q'uil existe un moyen de compter les digits à partir de la droite ?

Merci.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 569
3 févr. 2010 à 10:52
Re,

Une piste?!!!
Pourquoi pas utiliser l'opérateur de décalage (il me semble qu'il existe aussi en awk)

https://www.commentcamarche.net/faq/10440-conversion-d-une-adresse-ip-en-entier-32-bits
https://www.commentcamarche.net/faq/10443-conversion-d-un-nombre-entier-32-bits-en-ip
0
dubcek Messages postés 18755 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 14 novembre 2024 5 621
3 févr. 2010 à 12:34
on teste si la valeur est < 0, et on elimine ffffffff : $i < 0 ? n=9:n=0; ip=substr(sprintf("%x",$i),n)
$ echo -1139627840 320733888 | gawk --posix '{for(i=1;i<=NF;i++){$i < 0 ? n=9:n=0; ip=substr(sprintf("%x",$i),n);printf("%d.%d.%d.%d ", "0x" substr(ip,7,2), "0x" substr(ip,5,2), "0x" substr(ip,3,2), "0x" substr(ip,1,2))};print ""}' 
192.168.18.188 192.2.30.19 


avec gawk --posix, il comprend printf("%d ", "0x" substr(ip,7,2) pour imprimer en décimal un hexa, sinon il faudrait faire : printf("%d ", strtonum("0x" substr(ip,7,2)) ...
$ echo  320733888 | gawk  '{ip=sprintf("%x",$1);printf("%d", "0x" substr(ip,7,2));print ""}' 
0
$ echo  320733888 | gawk --posix '{ip=sprintf("%x",$1);printf("%d", "0x" substr(ip,7,2));print ""}' 
192
$ 
$ echo  320733888 | gawk  '{ip=sprintf("%x",$1);printf("%d", strtonum("0x" substr(ip,7,2)));print ""}' 
192
$ 
0
dubcek Messages postés 18755 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 14 novembre 2024 5 621
3 févr. 2010 à 15:04
ou aussi
$ echo -1139627840 320733888|gawk '{for(i=1;i<=NF;i++){ip=sprintf("%x",and($i,0x00000000ffffffff));for(n=7;n>0;n-=2){printf("%d",strtonum("0x" substr(ip,n,2)));if(n>1)printf(".")};print ""}}' 
192.168.18.188
192.2.30.19
$ 
0
Alright !

Bon alors voilà ce que j'ai fait finalement :
- testfile contient les lignes d'exemple ci-dessus.
- ligne de commande : cat testfile | awk --non-decimal-data -F, 'BEGIN{date[1]=5;date[2]=48;date[3]=49;ipaddr[1]=8;ipaddr[2]=14;ipaddr[3]=22;ipaddr[4]=29;ipaddr[5]=36;ipaddr[6]=44};{for(i in date){$(date[i])=strftime("%F %X", $(date[i]))};for(j in ipaddr){$(ipaddr[j]) < 0 ? n=9:n=0;ip=substr(sprintf("%8x",$(ipaddr[j])),n);$(ipaddr[j])=sprintf("%d.%d.%d.%d ", "0x"substr(ip,7,2), "0x"substr(ip,5,2), "0x"substr(ip,3,2), "0x"substr(ip,1,2))};print $0}'
- résultat :
1 2 42698 44111624 2008-05-27 14:03:47 2 0 192.16.48.225 "95011" "95011" 0 17 4 0.0.0.0 0 0 0 0 0 0 0 0.0.0.0 0 "0" "0" 44111628 2 0 192.16.224.0 "54790" "98500004" "" 0 0 4 0.0.0.0 0 0 0 0 0 0 0 0.0.0.0 0 "0" "0" 1970-01-01 01:00:00 2008-05-27 14:03:48 "54790" "0aaaa261-d245-4264-b09d-6ac17129a5e4" "PBX_FR-PBX" "_Globals_DNs" "_Globals_DNs" "PBX_FR-PBX" 0 "SEP001E4AAAAAA7" "IPMA-RP" 12 0 1 1 18 18 0 "Cluster1" 1 "" "" 0 "" 3 1 0 0 0 0 ""
1 2 42699 44111631 2008-05-27 14:04:06 2 0 192.16.48.225 "95011" "95011" 0 1 4 0.0.0.0 0 0 0 0 0 0 0 0.0.0.0 0 "0" "0" 44111632 0 0 0.0.0.0 "54576" "54576" "" 0 0 4 0.0.0.0 0 0 0 0 0 0 0 0.0.0.0 0 "0" "0" 1970-01-01 01:00:00 2008-05-27 14:04:07 "54576" "558aaaa5-225c-4f4c-a4df-9181e09c2f1f" "" "_Globals_DNs" "" "" 0 "SEP001E4AAAAAA7" "" 13 0 0 0 0 0 0 "Cluster1" 0 "" "" 0 "" 3 0 0 0 0 0 ""
1 2 42696 44111613 2008-05-27 14:01:14 2 0 192.16.28.203 "49240" "49240" 0 0 4 192.16.28.203 18454 4 20 0 0 0 0 0.0.0.0 0 "0" "0" 44111614 2 30 192.2.30.19 "00142148396" "00142148396" "" 0 16 4 192.2.30.19 18380 4 20 0 0 0 0 0.0.0.0 0 "0" "0" 2008-05-27 14:01:17 2008-05-27 14:04:28 "00142148396" "0abaaaa7-2990-48df-81d7-e9d015186857" "PBX_PSTN-FR-Granite" "_Globals_DNs" "PBX_PSTN-FR-Granite" "PBX_PSTN-FR" 191 "SEP001E7ACAAAA1" "S0/SU0/DS1-1@DEF-C50" 0 12 0 0 0 0 0 "Cluster1" 0 "" "" 0 "" 3 1 0 0 64 64 ""

Donc mes dates sont devenues lisibles, et les adresses ip aussi. :o)

Pour info :
- la fonction strtonum() marche bien aussi, cependant il aurait fallu l'écrire 4 fois pour envelopper les "0x" substr(ip,x,y), donc j'ai préféré réutiliser l'option --non-decimal-data qui marche chez moi et donne le même résultat.
- l'adresse ip en 0.12.1.46 venait du fait que la conversion en hexa du décimal 320733888 tient sur 7 digits, ce qui est a priori résolu en forçant le format de l'affichage avec sprintf("%8x",$(ipaddr[j])).

Un grand merci à tous les deux pour votre aide précieuse et pour le temps que vous avez bien voulu consacrer à mon problème !!!
Félicitations aussi pour votre maitrise de awk ;)


Nicooo
0
dubcek Messages postés 18755 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 14 novembre 2024 5 621
3 févr. 2010 à 16:09
- la fonction strtonum() marche bien aussi, cependant il aurait fallu l'écrire 4 fois pour
au post#11, j'ai mis une boucle : for(n=7;n>0;n-=2){printf("%d",strtonum("0x" substr(ip,n,2)))

visiblement, gawk traite les int sur 64bits, awk sur 32
$ echo -1 | gawk  '{printf("%u\n", $1)}'
18446744073709551615
$ echo -1 | awk  '{printf("%u\n", $1)}'
4294967295
$ 
0
Ha, j'ai du lire un peu vite,sorry.

Merci pour tes éclairages !!

Nicooo
0